PayPal Integration Guide
Connect your PayPal account to ExisOne to automatically generate and email license keys when customers purchase on your website.
1. Create a PayPal REST App
- Log into PayPal Developer Dashboard.
- Go to Apps & Credentials.
- Select Sandbox for testing (switch to Live later for production).
- Click Create App and give it a name.
- Copy your Client ID and Secret (click "Show" to reveal).
2. Configure PayPal Webhook
- In your PayPal app, scroll down to Webhooks and click Add Webhook.
- Set the Webhook URL to:
https://www.exisone.com/api/paypal/webhook - Select these events:
PAYMENT.CAPTURE.COMPLETED- Triggers license generation for one-time paymentsBILLING.SUBSCRIPTION.ACTIVATED- Triggers license generation for new subscriptionsPAYMENT.SALE.COMPLETED- Triggers license generation for subscription renewals
- Save the webhook and copy the Webhook ID (shown in the webhook list).
3. Register Your PayPal App in ExisOne
- In ExisOne, go to PayPal Integration > PayPal Apps and Events.
- Click New App and enter:
- Webhook ID - from step 2
- Client ID - from step 1
- Client Secret - from step 1
- Set Is Live to match your credentials (unchecked for Sandbox, checked for Live).
- Check Active and Default.
- 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:
- Auto Email: Check this box to automatically email license keys after purchase.
- Default License Days: Set to 0 for Perpetual, or a number of days for time-limited licenses.
- Email Template (optional): Customize the email sent to customers.
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.
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>
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)
| Field | Location | Description |
|---|---|---|
custom_id | purchase_units[0].custom_id | Preferred method - ExisOne Product ID |
sku | purchase_units[0].items[0].sku | Alternative - ExisOne Product ID in item SKU |
quantity | purchase_units[0].items[0].quantity | Number of licenses to generate |
7b. Subscription Licenses
For subscription-based licensing (e.g., annual renewals), ExisOne generates a new license key on each billing cycle - both on initial signup and every renewal.
custom_id when creating the subscription. This is where ExisOne reads the Product ID from. Format: "productId" or "productId:quantity" for multiple licenses.
PayPal Subscription Setup
- In PayPal, create a Product under Products & Plans
- Create a Billing Plan for that product (e.g., $99/year)
- When creating subscriptions, include
custom_idwith 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 (Annual)
| |
|-- BILLING.SUBSCRIPTION.ACTIVATED --> |-- PAYMENT.SALE.COMPLETED ------->
| |
| ExisOne fetches subscription from | ExisOne fetches subscription
| PayPal API, reads custom_id | from PayPal, reads custom_id
| |
| Generates license key(s) | Generates NEW license key(s)
| Emails to subscriber | Emails to subscriber
| |
Where to Put the Product ID (Subscriptions)
| Field | Format | Description |
|---|---|---|
custom_id | "42" | ExisOne Product ID only (1 license) |
custom_id | "42:5" | ExisOne Product ID with quantity (5 licenses) |
8. Test Your Integration
- Make sure you're using Sandbox credentials.
- Trigger a purchase from your website.
- Log into PayPal Sandbox with a test buyer account to complete payment.
- Check PayPal Apps and Events in ExisOne - you should see the webhook event.
- Verify the license key appears under License Keys in the ExisOne dashboard.
- 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
- One-time payments: The
custom_idor itemskumust contain your ExisOne Product ID. - Subscriptions: The
custom_idon the subscription holds the Product ID. Format:"42"or"42:5"for quantity. - Quantity is read from the item's
quantityfield, subscriptioncustom_id, plan name, or calculated from total amount / product price. - Perpetual licenses (Default License Days = 0) never expire.
- Emails use the product's template (placeholders:
{{ActivationKey}},{{ProductName}},{{CustomerEmail}},{{PaymentProcessorCustomerName}}).
Troubleshooting
Webhook not received
- Verify the webhook URL is exactly
https://www.exisone.com/api/paypal/webhook - Check that the webhook is enabled and listening for the required events:
PAYMENT.CAPTURE.COMPLETEDfor one-time paymentsBILLING.SUBSCRIPTION.ACTIVATEDandPAYMENT.SALE.COMPLETEDfor subscriptions
- In PayPal Developer Dashboard, go to Webhooks and check the event delivery log
License not generated (one-time payments)
- Verify your PayPal app is registered in ExisOne with the correct Webhook ID
- Check that the order includes
custom_idor itemskuwith a valid Product ID - Ensure the product exists in ExisOne and has Auto Email enabled
- Check the PayPal Events list for error details
Subscription renewals not generating license keys
- Ensure the subscription has a
custom_idset with the ExisOne Product ID - The
custom_idmust be set when creating the subscription - it cannot be added later - Check that your webhook is listening for
PAYMENT.SALE.COMPLETEDevents - Verify the PayPal app credentials (Client ID/Secret) are correct for API calls to fetch subscription details
- View the event in PayPal Events to check for errors
Event log
- View events in ExisOne at PayPal Integration > PayPal Apps and Events
- Click on an event to see the raw JSON payload for debugging
10. Setup Checklist
Before going live, verify all items are complete:
| Step | Where | Status |
|---|---|---|
| PayPal REST app created | PayPal Developer Dashboard | |
| Webhook created pointing to ExisOne | PayPal Developer Dashboard | |
| PayPal app registered with Webhook ID and credentials | ExisOne > PayPal Apps | |
| Product created in ExisOne | ExisOne > Products | |
| Product has Auto Email enabled | ExisOne > Products | |
| Email template configured | ExisOne > Products > Email Template | |
| Website integration includes productId in custom_id or sku | Your website code | |
| Test purchase completed successfully (Sandbox) | Your website | |
| Webhook received in ExisOne | ExisOne > PayPal Events | |
| License key generated and emailed | ExisOne > License Keys |
11. Go Live
- In PayPal Developer Dashboard, switch to Live mode.
- Create a new webhook pointing to
https://www.exisone.com/api/paypal/webhookwith live credentials. - In ExisOne, add a new PayPal app with your Live credentials and Webhook ID. Check Is Live and Active.
- Update your website to use your Live Client ID.
- Make a real purchase (you can refund it) to verify the complete flow works in production.