How To: Implement PayPal Payments

Learn how to accept PayPal payments via the prebuilt checkout UI, the Coinflow SDK button, or the Coinflow API.

Overview

This guide covers the three ways to accept PayPal with Coinflow:

  1. Prebuilt Checkout UI (iframe) — PayPal appears automatically inside Coinflow’s hosted checkout.
  2. CoinflowPayPalButton SDK component — Embed the PayPal button in your own checkout UI.
  3. Direct API + PayPal SDK — Render PayPal’s own SDK and call Coinflow’s checkout endpoints directly.

Before using any of these options, PayPal must be enabled on your Coinflow account. Contact your Coinflow integrations team to have PayPal configured.

Option 1: Prebuilt Checkout UI (iframe)

This is the simplest path. If you already embed Coinflow’s hosted checkout, there is nothing to build — once PayPal is enabled on your account, the PayPal button automatically appears alongside your other payment methods in the checkout iframe.

1

Request PayPal activation

Contact your Coinflow integrations team to enable PayPal on your account.

2

Confirm it appears

Load your existing Coinflow checkout. The PayPal button will render automatically with no code changes required.

If you have not yet integrated the hosted checkout, see Implement Checkout for how to embed the CoinflowPurchase component.

Option 2: CoinflowPayPalButton SDK Component

Use the CoinflowPayPalButton component when you have built your own checkout UI but want Coinflow to manage the PayPal order creation, approval overlay, and settlement. You render a single React component where you want the PayPal button to appear, and Coinflow handles the rest inside a secure iframe.

1

Install the SDK

$npm install @coinflowlabs/react
2

Render an overlay target

When the PayPal approval flow opens, the button expands to fill an element you control (for example, a full-screen backdrop). Render that element and give it an id, then pass the same id to the component via overlayId.

3

Render the button

Drop CoinflowPayPalButton into your checkout and handle the onApprove callback to drive your post-purchase UI.

1import {CoinflowPayPalButton, Currency} from '@coinflowlabs/react';
2
3function PayPalCheckout() {
4 return (
5 <>
6 <div id="paypal-overlay" />
7
8 <CoinflowPayPalButton
9 env={'sandbox'} // 'prod' | 'sandbox'
10 merchantId={'YOUR_MERCHANT_ID'}
11 sessionKey={'USER_SESSION_KEY'} // From: /api-reference/api-reference/authentication/get-session-key
12 subtotal={{cents: 500, currency: Currency.USD}}
13 email={'customer@example.com'}
14 overlayId={'paypal-overlay'}
15 handleHeightChange={(height) => {
16 /* size the button container */
17 }}
18 onApprove={({paymentId}) => {
19 // Payment approved — show your own confirmation screen.
20 console.log('PayPal payment complete', paymentId);
21 }}
22 onError={(error) => {
23 console.error('PayPal payment failed', error);
24 }}
25 />
26 </>
27 );
28}

Component Props

In addition to the standard Coinflow purchase props (env, merchantId, sessionKey, subtotal, handleHeightChange, etc.), the CoinflowPayPalButton accepts the following:

PropTypeRequiredDescription
overlayIdstringYesThe id of the element the PayPal approval overlay fills while it is open. On close, the button returns to its inline position.
onApprove(info: {paymentId: string}) => voidYesFires once the PayPal purchase is approved and complete. Use it to drive your own post-purchase UI.
emailstringOne identifier requiredEmail tied to the PayPal account, used to create the order.
phoneNumber{countryCode: string; nationalNumber: string}One identifier requiredPhone number tied to the PayPal account, used to create the order.
tokenstringOne identifier requiredA saved PayPal account token, used to create the order directly.
onError(error) => voidNoFires when the PayPal payment fails.

The button requires at least one customer identifier — email, phoneNumber, or token. Until one is provided, the button renders disabled.

Option 3: Direct API + PayPal SDK

This is the most flexible path. You render PayPal’s own button using the PayPal JS SDK and call Coinflow’s PayPal checkout endpoints to create the order. Use this when you need full control over the PayPal experience.

1

Fetch your PayPal merchant ID and client ID

Call the public Merchant View endpoint, GET /merchant/view/v2/{merchantId}, and read both merchant.paypalMerchantId and merchant.paypalClientId from the response. These are passed to the PayPal SDK as the merchant-id and client-id respectively. This is the same endpoint Coinflow’s hosted checkout uses to load your configuration, so no authentication is required — only your merchantId.

$curl --location 'https://api-sandbox.coinflow.cash/merchant/view/v2/<YOUR_MERCHANT_ID>' \
>--header 'accept: application/json'

Response (truncated):

1{
2 "merchant": {
3 "paypalMerchantId": "EXAMPLE_PAYPAL_MERCHANT_ID",
4 "paypalClientId": "EXAMPLE_PAYPAL_CLIENT_ID"
5 }
6}

paypalMerchantId is unique to your account and is set when the integrations team enables PayPal. paypalClientId is the PayPal client ID for the environment you are calling. The values above are examples only — read the real values from the API response.

The sandbox API (https://api-sandbox.coinflow.cash) returns the sandbox paypalClientId, and the production API (https://api.coinflow.cash) returns the production paypalClientId. Reading it from the API means there is no value to hard-code or hand off manually.

2

Initialize the PayPal SDK

Load the PayPal JS SDK using your paypalMerchantId as the merchant-id and your paypalClientId as the client-id. Initialize with intent=authorize, and set the partner attribution ID (BN code) to CoinflowLabsLimited_PSP via the data-partner-attribution-id attribute on the script tag.

1<script
2 src="https://www.paypal.com/sdk/js?client-id=<PAYPAL_CLIENT_ID>&merchant-id=<PAYPAL_MERCHANT_ID>&currency=USD&intent=authorize&components=buttons&disable-funding=paylater"
3 data-partner-attribution-id="CoinflowLabsLimited_PSP"
4></script>

You must pass the data-partner-attribution-id (BN code) CoinflowLabsLimited_PSP when loading the SDK. This identifies the integration as processed through Coinflow — payments will not be attributed correctly without it.

3

Create the order via Coinflow

Render the PayPal button and, in its createOrder callback, call the PayPal New Checkout endpoint. Coinflow creates the order and returns a paymentId.

$curl --location 'https://api-sandbox.coinflow.cash/api/checkout/paypal/<YOUR_MERCHANT_ID>' \
>--header 'accept: application/json' \
>--header 'content-type: application/json' \
>--header 'x-coinflow-auth-session-key: <YOUR_SESSION_KEY>' \
>--data-raw '{
> "subtotal": {
> "cents": 500,
> "currency": "USD"
> },
> "paypal": {
> "email": "customer@example.com"
> }
>}'

Response:

1{
2 "paymentId": "EXAMPLE_PAYMENT_ID"
3}

The paymentId returned by Coinflow is the PayPal order ID. Return it directly from your createOrder callback so the PayPal SDK uses it as the order ID — do not create a separate order with PayPal.

For customers paying with a previously saved PayPal account, call the PayPal Saved Checkout endpoint instead and pass the saved token.

1paypal.Buttons({
2 style: {layout: 'horizontal', shape: 'rect', label: 'paypal', height: 48},
3 createOrder: async () => {
4 const res = await fetch(
5 `https://api-sandbox.coinflow.cash/api/checkout/paypal/${merchantId}`,
6 {
7 method: 'POST',
8 headers: {
9 'content-type': 'application/json',
10 'x-coinflow-auth-session-key': sessionKey,
11 },
12 body: JSON.stringify({
13 subtotal: {cents: 500, currency: 'USD'},
14 paypal: {email: 'customer@example.com'},
15 }),
16 }
17 );
18 const {paymentId} = await res.json();
19 // Coinflow's paymentId IS the PayPal order ID — return it directly.
20 return paymentId;
21 },
22 onApprove: async (data) => {
23 // Payment approved by the customer in PayPal.
24 console.log('Approved', data.orderID);
25 },
26 onError: (err) => console.error(err),
27}).render('#paypal-button-container');
4

Confirm settlement with webhooks

Configure your webhook endpoint to listen for Settled events. This is the recommended way to confirm a payment has completed.

Important Notes

Account Configuration: PayPal must be enabled on your account by the Coinflow integrations team before any of these options will work.

Sandbox vs. Production: The paypalClientId returned by the API is scoped to the environment you call — the sandbox API returns the sandbox client ID and the production API returns the production client ID. When going live, switch to the production API base URL (https://api.coinflow.cash) and use the paypalClientId it returns.

Settlement: Regardless of which integration path you choose, funds are delivered to your configured settlement location, and the recommended way to confirm completion is by listening for Settled webhook events.