PayPal Integration Guide

Connect your PayPal account to ExisOne to automatically generate and email license keys when customers purchase on your website.

How it works: You integrate PayPal directly on your website using PayPal's API. When a customer pays, PayPal sends a webhook to ExisOne, which generates a license key and emails it to your customer automatically.

1. Create a PayPal REST App

  1. Log into PayPal Developer Dashboard.
  2. Go to Apps & Credentials.
  3. Select Sandbox for testing (switch to Live later for production).
  4. Click Create App and give it a name.
  5. Copy your Client ID and Secret (click "Show" to reveal).

2. Configure PayPal Webhook

  1. In your PayPal app, scroll down to Webhooks and click Add Webhook.
  2. Set the Webhook URL to: https://www.exisone.com/api/paypal/webhook
  3. Select these events:
    • PAYMENT.CAPTURE.COMPLETED - Triggers license generation for one-time payments
    • BILLING.SUBSCRIPTION.ACTIVATED - Triggers license generation for new subscriptions
    • PAYMENT.SALE.COMPLETED - Triggers license generation for subscription renewals
  4. Save the webhook and copy the Webhook ID (shown in the webhook list).
Note: The Webhook ID is how ExisOne identifies your account when PayPal sends events. Each webhook has a unique ID.

3. Register Your PayPal App in ExisOne

  1. In ExisOne, go to PayPal Integration > PayPal Apps and Events.
  2. Click New App and enter:
    • Webhook ID - from step 2
    • Client ID - from step 1
    • Client Secret - from step 1
  3. Set Is Live to match your credentials (unchecked for Sandbox, checked for Live).
  4. Check Active and Default.
  5. Save the app.

ExisOne uses the Webhook ID to match incoming webhooks to your account.

4. Configure Your Product in ExisOne

For each product you sell, configure these settings on the Products page:

4b. Configure Subscription Plan Mappings (Optional)

If you want to sell subscription-based licenses, you can map your ExisOne products to PayPal Plan IDs:

  1. In PayPal Dashboard, go to Products & Plans and create a subscription plan.
  2. Copy the Plan ID (starts with P-...).
  3. In ExisOne, go to PayPal Apps and Events.
  4. Scroll to Subscription Plan Mappings.
  5. Click Save after entering:
    • PayPal App - Select your PayPal app
    • Product - Select your ExisOne product
    • PayPal Plan ID - Paste the Plan ID from PayPal
    • Display Name (optional) - e.g., "Monthly", "Annual"
    • Display Price (optional) - e.g., 9.99
When to use this: Use subscription plan mappings to keep track of which PayPal plans correspond to which ExisOne products. This helps organize your subscription tiers (e.g., "Starter" at $9.99/month and "Pro" at $29.99/month for the same product).

5. Find Your Product ID

You'll need your Product ID to include in PayPal orders. Find it on the Products page - it's displayed in the ID column.

Example: If your "Pro License" product shows ID 42, you'll use "custom_id": "42" in your PayPal order.

6. How the Payment Flow Works

Your Website                        PayPal                         ExisOne
      |                                 |                               |
      |-- Create order --------------->|                               |
      |   (using PayPal API directly)   |                               |
      |   custom_id: "42"               |                               |
      |                                 |                               |
      |   Customer approves payment     |                               |
      |                                 |                               |
      |-- Capture order -------------->|                               |
      |                                 |                               |
      |                                 |-- Webhook ------------------ >|
      |                                 |   PAYMENT.CAPTURE.COMPLETED   |
      |                                 |                               |
      |                                 |   Match app by Webhook ID --->|
      |                                 |   Extract productId from      |
      |                                 |   custom_id or item sku       |
      |                                 |                               |
      |                                 |   Generate license key        |
      |                                 |                               |
      |                                 |   Email key to customer       |
      |                                 |                               |

7. Integrate PayPal on Your Website

Use PayPal's API directly on your website. The key is to include your ExisOne productId in the order's custom_id field or item sku.

Server-Side: Create Order (Node.js Example)
const paypal = require('@paypal/checkout-server-sdk');

// Configure PayPal environment
const environment = new paypal.core.SandboxEnvironment(
    'YOUR_CLIENT_ID',
    'YOUR_CLIENT_SECRET'
);
const client = new paypal.core.PayPalHttpClient(environment);

app.post('/create-paypal-order', async (req, res) => {
    const request = new paypal.orders.OrdersCreateRequest();
    request.prefer("return=representation");
    request.requestBody({
        intent: 'CAPTURE',
        purchase_units: [{
            custom_id: '42',           // Your ExisOne Product ID - REQUIRED
            amount: {
                currency_code: 'USD',
                value: '29.99',
                breakdown: {
                    item_total: { currency_code: 'USD', value: '29.99' }
                }
            },
            items: [{
                name: 'Pro License',
                sku: '42',             // Alternative: Product ID in SKU
                quantity: '1',
                unit_amount: { currency_code: 'USD', value: '29.99' }
            }]
        }]
    });

    const order = await client.execute(request);
    res.json({ id: order.result.id });
});

app.post('/capture-paypal-order', async (req, res) => {
    const { orderID } = req.body;
    const request = new paypal.orders.OrdersCaptureRequest(orderID);
    const capture = await client.execute(request);
    res.json({ status: capture.result.status });
});
Client-Side: PayPal Buttons (JavaScript)
<div id="paypal-button-container"></div>

<script src="https://www.paypal.com/sdk/js?client-id=YOUR_CLIENT_ID"></script>
<script>
paypal.Buttons({
    createOrder: async () => {
        const response = await fetch('/create-paypal-order', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' }
        });
        const order = await response.json();
        return order.id;
    },
    onApprove: async (data) => {
        const response = await fetch('/capture-paypal-order', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ orderID: data.orderID })
        });
        const result = await response.json();
        if (result.status === 'COMPLETED') {
            alert('Payment successful! Check your email for your license key.');
        }
    }
}).render('#paypal-button-container');
</script>
Important: The custom_id field (or item sku) must contain your ExisOne Product ID. Without it, ExisOne cannot determine which product to generate a license for.
Where to Put the Product ID (One-Time Payments)
FieldLocationDescription
custom_idpurchase_units[0].custom_idPreferred method - ExisOne Product ID
skupurchase_units[0].items[0].skuAlternative - ExisOne Product ID in item SKU
quantitypurchase_units[0].items[0].quantityNumber of licenses to generate

7b. Subscription Licenses

For subscription-based licensing (e.g., monthly or annual renewals), ExisOne issues a license key on the initial BILLING.SUBSCRIPTION.ACTIVATED event and then extends the same key's expiration date on every renewal PAYMENT.SALE.COMPLETED event. The subscriber never has to swap activation keys.

How extension works: On renewal, the existing license's ExpirationDate is bumped by the plan's DurationDays (or the product's DefaultLicenseDays if no plan is specified). Any unused time from the prior period is preserved — the new expiration is max(now, existingExpiration) + days.
Important: For subscriptions, you must set the custom_id when creating the subscription. This is where ExisOne reads the Product ID from. Format: "productId" or "productId:quantity" for multiple licenses. The PayPal subscription.id is then stored on each issued license so renewals can find it.
Duplicate-event handling: PayPal fires both BILLING.SUBSCRIPTION.ACTIVATED and PAYMENT.SALE.COMPLETED for the initial payment. ExisOne treats the second event as a duplicate of the first when the most recent license tagged with this subscription.id is younger than half the billing period; this prevents the initial period from being extended twice. Subsequent renewals always pass the threshold and extend normally.
Legacy subscriptions: Subscriptions created before the database upgrade (no PayPalSubscriptionId stored on the license) automatically fall back to the old behavior — a fresh key is minted on each renewal. Existing customers are not broken.
Subscription Loyalty: Convert to Perpetual After N Payments

On the product configuration page, the Convert to Perpetual After N Payments setting lets you reward long-term subscribers. Set the threshold (e.g. 6) and after that many successful payments the customer's license becomes permanent — the activation key never expires and the PayPal subscription is automatically cancelled so they aren't billed again.

PayPal Subscription Setup
  1. In PayPal, create a Product under Products & Plans
  2. Create a Billing Plan for that product (e.g., $99/year)
  3. When creating subscriptions, include custom_id with your ExisOne Product ID
Server-Side: Create Subscription (Node.js Example)
const paypal = require('@paypal/checkout-server-sdk');

app.post('/create-subscription', async (req, res) => {
    const request = new paypal.subscriptions.SubscriptionsCreateRequest();
    request.requestBody({
        plan_id: 'P-XXXXXXXXXXXXXXXXXXXXXXXX',  // Your PayPal Plan ID
        custom_id: '42',                         // ExisOne Product ID - REQUIRED
        // Or for multiple licenses: '42:5' (productId:quantity)
        subscriber: {
            email_address: req.body.email
        },
        application_context: {
            return_url: 'https://yoursite.com/success',
            cancel_url: 'https://yoursite.com/cancel'
        }
    });

    const subscription = await client.execute(request);
    res.json({ id: subscription.result.id, approvalUrl: subscription.result.links.find(l => l.rel === 'approve').href });
});
How Subscription Licensing Works
Initial Subscription                          Renewals (Monthly / Annual)
        |                                            |
        |-- BILLING.SUBSCRIPTION.ACTIVATED -->       |-- PAYMENT.SALE.COMPLETED ------->
        |                                            |
        |   ExisOne fetches subscription from        |   ExisOne looks up license by
        |   PayPal API, reads custom_id              |   PayPalSubscriptionId
        |                                            |
        |   Generates license key(s)                 |   Extends ExpirationDate on the
        |   Tags each with subscription.id           |   SAME key (no new key issued)
        |   Emails to subscriber                     |   Emails renewal confirmation
        |                                            |
Where to Put the Product ID (Subscriptions)
FieldFormatDescription
custom_id"42"ExisOne Product ID only (1 license)
custom_id"42:5"ExisOne Product ID with quantity (5 licenses)
Tip: You can also encode quantity in the PayPal Plan name (e.g., "5-License Annual Plan") and ExisOne will parse it as a fallback.

8. Test Your Integration

  1. Make sure you're using Sandbox credentials.
  2. Trigger a purchase from your website.
  3. Log into PayPal Sandbox with a test buyer account to complete payment.
  4. Check PayPal Apps and Events in ExisOne - you should see the webhook event.
  5. Verify the license key appears under License Keys in the ExisOne dashboard.
  6. Confirm the customer received the license key email (if Auto Email is enabled).

You can also use ExisOne's built-in Test Purchase page to verify webhook connectivity before integrating with your website.

PayPal Sandbox Test Accounts

In PayPal Developer Dashboard, go to Sandbox > Accounts to find or create test buyer/seller accounts for testing.

9. Notes

Troubleshooting

Webhook not received
License not generated (one-time payments)
Subscription renewals not extending the license
Event log

10. Setup Checklist

Before going live, verify all items are complete:

StepWhereStatus
PayPal REST app createdPayPal Developer Dashboard
Webhook created pointing to ExisOnePayPal Developer Dashboard
PayPal app registered with Webhook ID and credentialsExisOne > PayPal Apps
Product created in ExisOneExisOne > Products
Product has Auto Email enabledExisOne > Products
Email template configuredExisOne > Products > Email Template
Subscription plan mappings configured (if using subscriptions)ExisOne > PayPal Apps > Plan Mappings
Website integration includes productId in custom_id or skuYour website code
Test purchase completed successfully (Sandbox)Your website
Webhook received in ExisOneExisOne > PayPal Events
License key generated and emailedExisOne > License Keys

11. Go Live

  1. In PayPal Developer Dashboard, switch to Live mode.
  2. Create a new webhook pointing to https://www.exisone.com/api/paypal/webhook with live credentials.
  3. In ExisOne, add a new PayPal app with your Live credentials and Webhook ID. Check Is Live and Active.
  4. Update your website to use your Live Client ID.
  5. Make a real purchase (you can refund it) to verify the complete flow works in production.