Standard payments with single-page applications for direct merchants

DocsCurrentLast updated: March 16th 2023, @ 11:23:55 am


Use this guide if your integration uses a single-page application to accept payments, built on a library or framework such as React, Vue, or Angular.

Know before you code

Complete the steps in Get started to get the following sandbox REST API credentials from the Developer Dashboard:

  • Client ID: Authenticates your account with PayPal and identifies an app in your sandbox.
  • Client secret: Authorizes an app in your sandbox. Keep this secret safe and don’t share it.
  • Access token.
  • Use Postman to explore and test PayPal APIs.

1. Add JavaScript SDK

The JavaScript SDK shows your payer's available payment methods on your checkout page.

Set up the PayPal checkout for your application by using the JavaScript SDK and handling the payer's interactions with the PayPal checkout button.

Place the following script tag in your index.html page based on how you plan to render payment buttons, so the paypal object is available when you need it in the checkout flow. You can render the payment buttons either immediately, or after user action, navigation, or other page change:

  1. Immediate
  2. On change
1<script src="https://www.paypal.com/sdk/js?client-id=YOUR_CLIENT_ID"></script>

Modify the code

Note: This sample code is optimized for JavaScript performance. To learn more about how to optimize the JavaScript SDK, see JavaScript SDK performance optimization.

  1. Copy the sample JavaScript SDK code and paste it into your checkout page.
  2. In the SDK code, replace YOUR_CLIENT_ID with your client ID.
  3. Pass parameters to replace default values, such as USD for currency. To learn more about the default values, see the JavaScript SDK script configuration.

2. Add payment buttons driver

Add payment buttons to your single-page application's library or framework:

  1. React
  2. Angular
  3. Angular 2
  4. Vue
1const PayPalButton = paypal.Buttons.driver("react", { React, ReactDOM });

3. Add payment buttons to app

Choose your JavaScript library or framework:

React

Component implementation and functional implementation.

Component implementation

1import React from "react";
2import ReactDOM from "react-dom"
3const PayPalButton = paypal.Buttons.driver("react", { React, ReactDOM });
4class YourComponent extends React.Component {
5 createOrder(data) {
6 // Order is created on the server and the order id is returned
7 return fetch("/my-server/create-paypal-order", {
8 method: "POST",
9 headers: {
10 "Content-Type": "application/json",
11 },
12 // use the "body" param to optionally pass additional order information
13 // like product skus and quantities
14 body: JSON.stringify({
15 cart: [
16 {
17 sku: "YOUR_PRODUCT_STOCK_KEEPING_UNIT",
18 quantity: "YOUR_PRODUCT_QUANTITY",
19 },
20 ],
21 }),
22 })
23 .then((response) => response.json())
24 .then((order) => order.id);
25 }
26 onApprove(data) {
27 // Order is captured on the server
28 return fetch("/my-server/capture-paypal-order", {
29 method: "POST",
30 headers: {
31 "Content-Type": "application/json",
32 },
33 body: JSON.stringify({
34 orderID: data.orderID
35 })
36 })
37 .then((response) => response.json());
38 }
39 render() {
40 return (
41 <PayPalButton
42 createOrder={(data, actions) => this.createOrder(data)}
43 onApprove={(data, actions) => this.onApprove(data)}
44 />
45 );
46 }
47}

Functional implementation

1import React from "react";
2import ReactDOM from "react-dom"
3const PayPalButton = paypal.Buttons.driver("react", { React, ReactDOM });
4function YourComponent() {
5 const createOrder = (data) => {
6 // Order is created on the server and the order id is returned
7 return fetch("/my-server/create-paypal-order", {
8 method: "POST",
9 headers: {
10 "Content-Type": "application/json",
11 },
12 // use the "body" param to optionally pass additional order information
13 // like product skus and quantities
14 body: JSON.stringify({
15 cart: [
16 {
17 sku: "YOUR_PRODUCT_STOCK_KEEPING_UNIT",
18 quantity: "YOUR_PRODUCT_QUANTITY",
19 },
20 ],
21 }),
22 })
23 .then((response) => response.json())
24 .then((order) => order.id);
25 };
26 const onApprove = (data) => {
27 // Order is captured on the server and the response is returned to the browser
28 return fetch("/my-server/capture-paypal-order", {
29 method: "POST",
30 headers: {
31 "Content-Type": "application/json",
32 },
33 body: JSON.stringify({
34 orderID: data.orderID
35 })
36 })
37 .then((response) => response.json());
38 };
39 return (
40 <PayPalButton
41 createOrder={(data) => createOrder(data, actions)}
42 onApprove={(data) => onApprove(data, actions)}
43 />
44 );
45}

Angular

1paypal.Buttons.driver("angular", window.angular);
2angular
3 .module("app", ["paypal-buttons"])
4 .controller("appController", function ($scope) {
5 $scope.opts = {
6 createOrder: function (data) {
7 // Order is created on the server and the order id is returned
8 return fetch("/my-server/create-paypal-order", {
9 method: "POST",
10 headers: {
11 "Content-Type": "application/json",
12 },
13 // use the "body" param to optionally pass additional order information
14 // like product skus and quantities
15 body: JSON.stringify({
16 cart: [
17 {
18 sku: "YOUR_PRODUCT_STOCK_KEEPING_UNIT",
19 quantity: "YOUR_PRODUCT_QUANTITY",
20 },
21 ],
22 }),
23 })
24 .then((response) => response.json())
25 .then((order) => order.id);
26 },
27 onApprove: function (data) {
28 // Order is captured on the server
29 return fetch("/my-server/capture-paypal-order", {
30 method: "POST",
31 headers: {
32 "Content-Type": "application/json",
33 },
34 body: JSON.stringify({
35 orderID: data.orderID
36 })
37 })
38 .then((response) => response.json());
39 },
40 };
41 });
1<body ng-app="app" ng-controller="appController">
2 <paypal-buttons props="opts"></paypal-buttons>
3</body>

Angular 2

1(function () {
2 const PayPalButton = paypal.Buttons.driver("angular2", ng.core);
3 const appComponent = ng.core
4 .Component({
5 selector: "my-app",
6 template:
7 <div id="app">
8 <paypal-buttons [props]="{
9 createOrder: createOrder,
10 onApprove: onApprove
11 }"></paypal-buttons>
12 </div>
13 ,
14 })
15 .Class({
16 constructor: function () {
17 this.createOrder = (function(data) {
18 // Order is created on the server and the order id is returned
19 return fetch("/my-server/create-paypal-order", {
20 method: "POST",
21 headers: {
22 "Content-Type": "application/json",
23 },
24 // use the "body" param to optionally pass additional order information
25 // like product skus and quantities
26 body: JSON.stringify({
27 cart: [
28 {
29 sku: "YOUR_PRODUCT_STOCK_KEEPING_UNIT",
30 quantity: "YOUR_PRODUCT_QUANTITY",
31 },
32 ],
33 }),
34 })
35 .then((response) => response.json())
36 .then((order) => order.id);
37 }).bind(this);
38 this.onApprove = (function(data, actions) {
39 // Order is captured on the server
40 return fetch("/my-server/capture-paypal-order", {
41 method: "POST",
42 headers: {
43 "Content-Type": "application/json",
44 },
45 body: JSON.stringify({
46 orderID: data.orderID
47 })
48 })
49 .then((response) => response.json());
50 }).bind(this);
51 });
52 }});
53 const appModule = ng.core
54 .NgModule({
55 imports: [ng.platformBrowser.BrowserModule, PayPalButton],
56 declarations: [appComponent],
57 bootstrap: [appComponent],
58 })
59 .Class({
60 constructor: function () {},
61 });
62 document.addEventListener("DOMContentLoaded", function () {
63 ng.platformBrowserDynamic
64 .platformBrowserDynamic()
65 .bootstrapModule(appModule);
66 });
67})();
1(function () {
2 const PayPalButton = paypal.Buttons.driver("angular2", ng.core);
3 const appComponent = ng.core
4 .Component({
5 selector: "my-app",
6 template:
7 <div id="app">
8 <paypal-buttons [props]="{
9 createOrder: createOrder,
10 onApprove: onApprove
11 }"></paypal-buttons>
12 </div>
13 ,
14 })
15 .Class({
16 constructor: function () {
17 this.createOrder = (function(data) {
18 // Order is created on the server and the order id is returned
19 return fetch("/my-server/create-paypal-order", {
20 method: "POST",
21 headers: {
22 "Content-Type": "application/json",
23 },
24 // use the "body" param to optionally pass additional order information
25 // like product skus and quantities
26 body: JSON.stringify({
27 cart: [
28 {
29 sku: "YOUR_PRODUCT_STOCK_KEEPING_UNIT",
30 quantity: "YOUR_PRODUCT_QUANTITY",
31 },
32 ],
33 }),
34 })
35 .then((response) => response.json())
36 .then((order) => order.id);
37 }).bind(this);
38 this.onApprove = (function(data) {
39 // Order is captured on the server
40 return fetch("/my-server/capture-paypal-order", {
41 method: "POST",
42 headers: {
43 "Content-Type": "application/json",
44 },
45 body: JSON.stringify({
46 orderID: data.orderID
47 })
48 })
49 .then((response) => response.json());
50 }).bind(this);
51 });
52 }});
53 const appModule = ng.core
54 .NgModule({
55 imports: [ng.platformBrowser.BrowserModule, PayPalButton],
56 declarations: [appComponent],
57 bootstrap: [appComponent],
58 })
59 .Class({
60 constructor: function () {},
61 });
62 document.addEventListener("DOMContentLoaded", function () {
63 ng.platformBrowserDynamic
64 .platformBrowserDynamic()
65 .bootstrapModule(appModule);
66 });
67})();

Angular 2 using TypeScript

1@ng.core.Component({
2 selector: 'my-app',
3 template:
4 <div id="app">
5 <paypal-buttons [props]="{
6 createOrder: createOrder,
7 onApprove: onApprove
8 }"></paypal-buttons>
9 </div>
10 ,
11})
12class AppComponent {
13 createOrder(data) {
14 // Order is created on the server and the order id is returned
15 return fetch("/my-server/create-paypal-order", {
16 method: "POST",
17 headers: {
18 "Content-Type": "application/json",
19 },
20 // use the "body" param to optionally pass additional order information
21 // like product skus and quantities
22 body: JSON.stringify({
23 cart: [
24 {
25 sku: "YOUR_PRODUCT_STOCK_KEEPING_UNIT",
26 quantity: "YOUR_PRODUCT_QUANTITY",
27 },
28 ],
29 }),
30 })
31 .then((response) => response.json())
32 .then((order) => order.id);
33 }
34 onApprove(data, actions) {
35 // Order is captured on the server
36 return fetch("/my-server/capture-paypal-order", {
37 method: "POST",
38 headers: {
39 "Content-Type": "application/json",
40 },
41 body: JSON.stringify({
42 orderID: data.orderID
43 })
44 })
45 .then((response) => response.json());
46 }
47}
48@ng.core.NgModule({
49 imports: [
50 ng.platformBrowser.BrowserModule,
51 paypal.Buttons.driver('angular2', ng.core)
52 ],
53 declarations: [
54 AppComponent
55 ],
56 bootstrap: [
57 AppComponent
58 ]
59})
60class AppModule {}
61ng.platformBrowserDynamic
62 .platformBrowserDynamic()
63 .bootstrapModule(AppModule);

Vue

1<div id="container">
2 <app></app>
3</div>
4<script>
5 const PayPalButton = paypal.Buttons.driver('vue', window.Vue)
6
7 Vue.component("app", {
8 // The style prop for the PayPal button should be passed in as `style-object` or `styleObject` to avoid conflict with Vue's `style` reserved prop.
9 template: `
10 <paypal-buttons :on-approve="onApprove" :create-order="createOrder" :on-shipping-change="onShippingChange" :on-error="onError" :style-object="style" />
11 `,
12 components: {
13 "paypal-buttons": PayPalButton,
14 },
15
16 computed: {
17 createOrder: function () {
18 return (data) => {
19 // Order is created on the server and the order id is returned
20 return fetch("/my-server/create-paypal-order", {
21 method: "POST",
22 headers: {
23 "Content-Type": "application/json",
24 },
25 // use the "body" param to optionally pass additional order information
26 // like product skus and quantities
27 body: JSON.stringify({
28 cart: [
29 {
30 sku: "YOUR_PRODUCT_STOCK_KEEPING_UNIT",
31 quantity: "YOUR_PRODUCT_QUANTITY",
32 },
33 ],
34 }),
35 })
36 .then((response) => response.json())
37 .then((order) => order.id);
38 }
39 },
40 onApprove: function () {
41 return (data) => {
42 // Order is captured on the server
43 return fetch("/my-server/capture-paypal-order", {
44 method: "POST",
45 headers: {
46 "Content-Type": "application/json",
47 },
48 body: JSON.stringify({
49 orderID: data.orderID
50 })
51 })
52 .then((response) => response.json());
53 }
54 },
55 onShippingChange: function () {
56 return (data, actions) => {
57 if (data.shipping_address.country_code !== 'US') {
58 return actions.reject();
59 }
60 return actions.resolve();
61 }
62 },
63 onError: function () {
64 return (err) => {
65 console.error(err);
66 window.location.href = "/your-error-page-here";
67 }
68 },
69 style: function () {
70 return {
71 shape: 'pill',
72 color: 'gold',
73 layout: 'horizontal',
74 label: 'paypal',
75 tagline: false,
76 }
77 },
78 },
79 });
80
81 const vm = new Vue({
82 el: "#container",
83 });
84</script>

Next steps

Test in the PayPal sandbox, then go live in PayPal's live environment.