Unified Pay Docs

Getting Started

  • Introduction
  • Getting Started

Payment Providers

  • Stripe
  • PayPal
  • Razorpay

Resources

  • API Reference
  • Best Practices
  • FAQ
DocumentationPayPal Integration

PayPal Integration

Complete guide for integrating PayPal payments with unified-pay-core

Documentation Team

This guide explains how to integrate the PayPal payment gateway using the unified-pay-core package. It covers setup, configuration, and key operations like creating payments, capturing payments, processing refunds, and handling webhooks.

Prerequisites

  • Node.js and npm installed.
  • A PayPal account with API credentials (Client ID and Secret).
  • A webhook ID for verifying webhook events.
  • Install the unified-pay-core package:
npm install unified-pay-core

Setup

Obtain PayPal Credentials

  1. Log in to your PayPal Developer Dashboard.
  2. Create a sandbox or live app to get your Client ID and Secret.
  3. Set up a webhook in the PayPal Dashboard and obtain the Webhook ID.

Install Dependencies

Ensure you have express and cors for the example server:

npm install express cors

Configure the Gateway

Initialize the PayPal gateway using the createGateways function.

const express = require("express");
const cors = require("cors");
const { createGateways } = require("unified-pay-core");

const app = express();
app.use(cors());
app.use("/webhook", express.raw({ type: "application/json" }));
app.use(express.json());

const { paypal } = createGateways([
  {
    gateway: "paypal",
    config: {
      apiKey: process.env.API_KEY,
      apiSecret: process.env.API_SECRET,
      environment: "sandbox",
      returnUrl: "https://example.com/success",
      cancelUrl: "http://example.com/cancel",
      webhookId: process.env.WEBHOOK_ID,
    },
  },
]);

Key Operations

1. Create a Payment

Initiate a payment request to create a PayPal order.

app.post("/create-payment", async (req, res) => {
  try {
    const { amount = 20 } = req.body;
    const payment = await paypal.processPayment(amount, "USD", {
      test_run: "Sample Payment",
    });
    res.json({
      success: true,
      transactionId: payment.transactionId,
      approveLink: payment.approveLink || null,
    });
  } catch (err) {
    console.error("Payment creation error:", err);
    res.status(500).json({ success: false, error: "Payment creation failed" });
  }
});

Input:

  • amount: Payment amount (e.g., 20 for $20.00).
  • currency: Currency code (e.g., "USD").
  • metadata: Optional key-value pairs for additional data.

Output:

  • transactionId: PayPal Order ID.
  • approveLink: URL for user approval (redirect the user to this link).

2. Capture a Payment

Capture a previously created order after user approval.

app.get("/return", async (req, res) => {
  try {
    const { token } = req.query;
    if (!token) return res.status(400).json({ error: "Missing token" });
    const capture = await paypal.capturePayment(token);
    const verification = await paypal.verifyPayment(capture.transactionId);
    res.json({ capture, verification });
  } catch (err) {
    console.error("Payment capture error:", err);
    res.status(500).json({ error: "Payment capture failed" });
  }
});

Input:

  • token: The Order ID from the payment creation response.

Output:

  • capture: Payment response with status, amount, and currency.
  • verification: Verification details confirming the payment status.

3. Process a Refund

Refund a captured payment (full or partial).

app.post("/refund", async (req, res) => {
  try {
    const { transactionId, amount } = req.body;
    if (!transactionId) {
      return res.status(400).json({ error: "transactionId is required" });
    }
    const refund = await paypal.processRefund(
      transactionId,
      amount ? parseFloat(amount) : undefined
    );
    res.json({
      success: refund.success,
      refundId: refund.refundId,
      transactionId,
      status: refund.status,
      amount: refund.amount,
      currency: refund.currency,
    });
  } catch (err) {
    console.error("Refund error:", err);
    res.status(500).json({ success: false, error: "Refund failed" });
  }
});

Input:

  • transactionId: The Capture ID from the payment capture response.
  • amount: Optional partial refund amount (e.g., 10 for $10.00).

Output:

  • refundId: PayPal Refund ID.
  • status: Refund status (succeeded, pending, failed, cancelled).

4. Verify a Payment

Verify the status of a captured payment.

const verification = await paypal.verifyPayment(transactionId);

Input:

  • transactionId: The Capture ID to verify.

Output:

  • verified: Boolean indicating if the verification was successful.
  • status: Payment status (succeeded, pending, failed).
  • isValid: Boolean indicating if the payment is valid (e.g., COMPLETED).

Warning:āš ļø The webhook implementation for paypal has unresolved bugs and is not production-ready. Use only in a sandbox/testing environment.

5. Handle Webhooks

Set up a webhook endpoint to receive and process PayPal events.

app.post("/webhook/paypal", async (req, res) => {
  try {
    const result = await paypal.handleWebhookEvent(req.body, req.headers);
    if (result === "INVALID") {
      return res.status(400).send("Invalid webhook signature");
    }
    const parsedBody = JSON.parse(req.body.toString());
    const eventType = parsedBody.event_type;
    switch (eventType) {
      case "PAYMENT.CAPTURE.COMPLETED":
        console.log("Payment captured:", parsedBody.resource.id);
        break;
      case "PAYMENT.CAPTURE.DENIED":
        console.log("Payment denied:", parsedBody.resource.id);
        break;
      default:
        console.log("Unhandled event:", eventType);
    }
    res.status(200).send("OK");
  } catch (error) {
    console.error("Webhook processing error:", error);
    res.status(500).send("Webhook processing failed");
  }
});

Setup:

  • Use express.raw({ type: "application/json" }) for the webhook route.
  • Configure the webhook URL in the PayPal Dashboard (e.g., https://yourdomain.com/webhook/paypal).

Supported Events:

  • PAYMENT.CAPTURE.COMPLETED
  • PAYMENT.CAPTURE.DENIED

Webhook Testing

  1. Use ngrok to expose your local server: ngrok http 3000.
  2. Update the webhook URL in the PayPal Dashboard.
  3. Test events like PAYMENT.CAPTURE.COMPLETED using PayPal's webhook simulator.

Notes

  • Order vs. Capture ID: processPayment returns an Order ID; capturePayment uses this Order ID and returns a Capture ID for refunds and verification.
  • Webhook ID: Ensure webhookId is set for signature verification.
  • Return/Cancel URLs: Configure returnUrl and cancelUrl for user redirection after approval or cancellation.
  • Error Handling: Check success and error fields in responses to handle failures.
Previous
Stripe Integration
Next
Razorpay Integration

Was this helpful?

Help us improve our documentation by providing feedback.