Pain with Paypal payment gateway integration
So a while ago I wanted to integrate PayPal’s payment gateway to accept international payments and guess what PayPal's developer API documentation is made just to confuse you and viciously bang your head at times 😒.
After having a couple of sleepless nights trying to figure out the most secure way to integrate Paypal, I had decided to write a blog about it (Ofcourse procrastinated a bit but here you are :p)
This blog explains the integration of “Paypal Standard Checkout” with your application. Reason for choosing Standard Checkout instead of having Pure server-based integration with a custom “Pay now” button was :
a. It creates trust amongst your Users when they Pay via Paypal directly instead of using a custom button and getting redirected to Paypal's payment gateway. The Paypal Smart Payment button gives your buyers a simplified and secure checkout experience.
b. We didn’t want users to leave our website (Of course you can auto-redirect once payment is done, but the alternative of having Paypal Smart Payment button was more UX optimal)
When it comes to Payment which is basically converting your potential lead to your customer you have to think through these decisions.
Integrate Paypal Smart Button with your server
Paypal provides Smart Button which you can integrate with your backend to create orders, capture orders, and perform relevant actions once the payment is successful.
The secured flow of Paypal payment gateway :
Smart button has the capability of creating order, capturing order on the frontend layer itself. But that is not the most secure way, as a user can alter the amount before the order gets created, thus tampering with your system. Also while creating an end-to-end application you want to keep track of all the orders and it’s status so that you are in a position to answer any questions related to payment failure and the amount getting deducted from your users.
The right way is :
- Create the order from the backend and pass it to the Smart button. This gives you power to create the order with all the required details and information you want to pass to PayPal, also populate your order’s table with its status as “CREATED” stating the order was created
- Let Paypal automatically capture payment for us. Once the order is created, the Smart button will open a modal that will ask the user to pay with the order which you had created from the backend. Once the user makes a payment, it’ll be automatically captured by the Smart button. Capturing a payment is essential as it tells Paypal that we authorize and validate this payment. Until and unless the payment is not captured, PayPal won’t allow you to withdraw that amount from your account.
- Send the details of captured payment to the backend. Once the payment is captured for us, we can send the “details” Object having all the relevant information and object Ids we need to verify the transaction and get its status to our backend. We call the orders API with the order Id provided by the frontend to get the status of the order and update the status in our DB against the order Id. We also fetch the payment details calling Payment Api with the payment Id passed from the frontend and store the response in our database. Orders and payment table helps us to debug any user queries and help us in accounting and auditing purposes.
Frontend
Tech stack — Nextjs with Typescript and tailwind for CSS
The core package which we have used interacts with Paypal and our backend with “Paypal smart button” is npm install @paypal/react-paypal-js
It gives us “createOrder” handler where we can create order from the backend and pass it to Paypal, which will further launch the Paypal payment Popup, the “onApprove” receive the action post-payment and we capture the payment(Smart button does it for us) here to complete the payment cycle. We also get object related to “payment details” having PaymentId and OrderId which we use at the backend for verification of Payment. The “onError” handler helps us with handling Paypal-related errors if any and updates the order at the backend marking it as Paypal with the status being “INCOMPLETE”.
Code here summarizes everything which we discussed related to frontend https://github.com/savannahar68/paypal-integration
Backend
Tech Stack — Nodejs with Typescript
The core package we used for calling Paypal’s rest API is @paypal/checkout-server-sdk
The rest calls which we use to query Paypal from the backend are :
- /v2/checkout/orders — For creating “digital order” for the request received from the “createOrder” handler on the frontend.
- /v2/checkout/orders/{order_id} — For capturing the order status post-payment is done and captured by “react-paypal-js” on the frontend’s “onApprove” handler, and “orderId” passed back to the backend in the payment details object.
- /v2/payments/captures/{capture_id} — For capturing the payment done on the frontend’s “onApprove” handler, and “paymentId” passed back to the backend in the payment details object.
Check out Orders and Payment v2 Api specs posted on developer.paypal.com for more details and customization if needed [links are available in the references section at the end of this blog post]
Code here summarizes everything which we discussed related to frontend https://github.com/savannahar68/paypal-integration/tree/main/backend
Once all the changes are done, test the end-to-end payment workflow. This is how it looks.
Special thanks to Yash and Naman for contributing to the project. Feel free to ping us on LinkedIn on Twitter to talk about code :)
Savan — Savan Nahar | LinkedIn | Twitter
Yash — Yash Shah | LinkedIn | Twitter
Naman — Naman Modi | LinkedIn | Twitter
References :