Search code examples
javascriptreactjspaypalpaypal-sandbox

How to integrate Paypal Javascript SDK for React SPA with backend server validation?


my idea is to integrate the Paypal Javascript SDK for the payment process in my React SPA. The process in the SPA is working and I'm receiving the Paypal webhook at m backend.

I'm trying to understand how to validate the payment status at my backend when the SPA has problems to send the Paypal payment staus to the backend.

In short the sequence is as follows

+-----+               +---------+                                     +---------------+                          +-----------+
| SPA |               | Paypal  |                                     | BackendServer |                          | Database  |
+-----+               +---------+                                     +---------------+                          +-----------+
   |                       |                                                  |                                        |
   | Initiate Payment      |                                                  |                                        |
   |-----------------      |                                                  |                                        |
   |                |      |                                                  |                                        |
   |<----------------      |                                                  |                                        |
   |                       |                                                  |                                        |
   | (1) createOrder       |                                                  |                                        |
   |---------------------->|                                                  |                                        |
   |                       |                                                  |                                        |
   |             onApprove |                                                  |                                        |
   |<----------------------|                                                  |                                        |
   |                       |                                                  |                                        |
   | (2) Create Payment Completed for User  [Payment For User]                |                                        |
   |------------------------------------------------------------------------->|                                        |
   |                       |                                                  |                                        |
   |                       |                                                  | Save  [Payment For User]               |
   |                       |                                                  |--------------------------------------->|
   |                       |                                                  |                                        |
   |                       |                                                  |                     [Payment For User] |
   |                       |                                                  |<---------------------------------------|
   |                       |                                                  |                                        |
   |                       |                                               OK |                                        |
   |<-------------------------------------------------------------------------|                                        |
   |                       |                                                  |                                        |
   |                       | (3) Webhhok->"PAYMENT.CAPTURE.COMPLETED"         |                                        |
   |                       |------------------------------------------------->|                                        |
   |                       |                                                  |                                        |
   |                       |                                                  | Check [Payment by Paypal ID]           |
   |                       |                                                  |--------------------------------------->|
   |                       |                                                  |                                        |
   |                       |                                                  |       [Payment by Paypal ID with User] |
   |                       |                                                  |<---------------------------------------|
   |                       |                                                  |                                        |
   |                       |                                                  | [Order for User for Payment]           |
   |                       |                                                  |--------------------------------------->|
   |                       |                                                  |                                        |
   |                       |                                                  |          [Order for User for Payment]  |
   |                       |                                                  |<---------------------------------------|
   |                       |                                                  |                                        |
   |                       |                        Email / Websocket message |                                        |
   |<-------------------------------------------------------------------------|                                        |
   |                       |                                                  |                                        |

The steps are as follows:

(1) Initiate the Paypal process for "createOrder" with "actions.order.create" as follows:

createOrder: (data, actions) => {
  return actions.order.create({
    intent: 'CAPTURE',
    // The object 'payer' seems only to be used for the login form from Paypal.
    payer: {
      name: {
        given_name: 'given_name for login',
        surname: 'surname for login',
      },
      email_address: 'PAYPAL EMAIL FOR LOGIN',
      id: 'PAYPAL ID',
    },
    purchase_units: [
      {
        description: product.description,
        amount: {
          currency_code: 'EUR',
          value: product.price,
        },
      },
    ],
  });
},


(2) After receieving the Paypal pament approvement, I'm sending the status to the backend server

onApprove: async (data, actions) => {
  const order = await actions.order.capture();
  sendOrderToBackend(order);
},

(3) I'm receiving the Paypal payment webhook and the informtion that the payment is succesful. After that I'm validating the order for the user by the "Paypal pament ID".

Question / Problem

I do not understand what happens when my SPA has problems to send the "Paypal payment staus" to my backend server in (2).

How could I validate in the backend to which user the "Paypal pament" belongs to?

I do not find any reference in the Paypal SDK docuemtation how to custom parameters. Do you know how I could send custom key/value parameters to Paypal and receive these in the webhook?

Thanks for your help!

P.s. for the sake of completeness see the Paypal SDK integration as follows:

window.paypal
  .Buttons({
    createOrder: (data, actions) => {
      return actions.order.create({
        intent: 'CAPTURE',
        payer: {
          name: {
            given_name: 'given_nameXXX',
            surname: 'surnameXXX',
          },
          email_address: 'test@gmail.com',
          id: 'realyonly13charcaters???',
        },
        purchase_units: [
          {
            description: product.description,
            amount: {
              currency_code: 'EUR',
              value: product.price,
            },
          },
        ],
      });
    },
    onApprove: async (data, actions) => {
      const order = await actions.order.capture();
      sendOrderToBackend(order);
    },
    onError: err => {
      setError(err);
      console.error(err);
    },
  })
  .render(paypalRef.current);

Solution

  • Use a server-side integration pattern. Here's the UI: https://developer.paypal.com/demo/checkout/#/pattern/server

    You'll need two routes on the backend, one for 'Set Up Transaction' and one for 'Capture Transaction', documented here: https://developer.paypal.com/docs/checkout/reference/server-integration/

    No webhooks are needed.