Skip to main content

Subscriptions

To implement recurring billing with PayPal, use the PayPal Subscriptions flow.

This differs from standard MIT recurring (cards, SEPA, etc.): PayPal controls when subscription charges run. You do not call POST /rest/payment again with original_transaction_id for each billing cycle.

Not supported: standard MIT recurring

PayPal does not support merchant-initiated recurring payments using recurring: 1 and original_transaction_id (the card-style flow where you trigger each subsequent charge).

For recurring billing with PayPal, use PayPal Subscriptions only: set recurring: 1, amount: 0, and paypal_plan_id on the initial payment. PayPal triggers future charges; our platform creates child transactions and sends postbacks to your postback_url.

Subscription workflow​

The diagram covers the whole lifecycle: the one-time setup you perform in PayPal, the subscription approval by the customer, the recurring charges PayPal triggers afterwards, and the changes you can apply later.

PayPal Subscriptions — setup, recurring charges, and changes
Payer (Customer)
Merchant
OneAPI
PayPal
PayPal
One-time setup, done directly with PayPal before any subscription exists
1. Create Catalogue Product and Plan (PayPal APIs)
2. Returns the paypal_plan_id
3. Subscribes on the merchant site
4. Create subscription (Payment API: recurring 1, amount 0, paypal_plan_id)
5. Creates the subscription for that plan
6. status: pending, client_action: redirect (action_data.url)
7. Redirect to the PayPal approval page
8. Logs in and approves the subscription
9. Approval confirmed — subscription is active
10. Postback: setup transaction is registered (amount 0)
11. Redirect to success_url or error_url
PayPal now triggers every billing cycle defined in the plan — you never call the Payment API again
12. Charges the payer on each billing cycle
13. Creates a child transaction for that charge
14. Postback per charge, with original_transaction_id
Charge postbacks go to the postback_url of the setup transaction
15. Cancel API or Revise API (plan, quantity, shipping)
16. Cancels or revises the subscription
Revise can return client_action: redirect when PayPal needs the payer to approve the change

Scroll horizontally if the diagram is wider than your screen.

  1. Merchant

    One-time setup, done directly with PayPal before any subscription exists

  2. 1
    MerchantPayPalPayPal

    1. Create Catalogue Product and Plan (PayPal APIs)

  3. 2
    PayPalPayPalMerchant

    2. Returns the paypal_plan_id

  4. 3
    Payer (Customer)Merchant

    3. Subscribes on the merchant site

  5. 4
    MerchantOneAPI

    4. Create subscription (Payment API: recurring 1, amount 0, paypal_plan_id)

  6. 5
    OneAPIPayPalPayPal

    5. Creates the subscription for that plan

  7. 6
    OneAPIMerchant

    6. status: pending, client_action: redirect (action_data.url)

  8. 7
    MerchantPayer (Customer)

    7. Redirect to the PayPal approval page

  9. 8
    Payer (Customer)PayPalPayPal

    8. Logs in and approves the subscription

  10. 9
    PayPalPayPalOneAPI

    9. Approval confirmed — subscription is active

  11. 10
    OneAPIMerchant

    10. Postback: setup transaction is registered (amount 0)

  12. 11
    OneAPIPayer (Customer)

    11. Redirect to success_url or error_url

  13. PayPalPayPal

    PayPal now triggers every billing cycle defined in the plan — you never call the Payment API again

  14. 12
    PayPalPayPalOneAPI

    12. Charges the payer on each billing cycle

  15. 13
    OneAPIOneAPI

    13. Creates a child transaction for that charge

  16. 14
    OneAPIMerchant

    14. Postback per charge, with original_transaction_id

  17. OneAPI

    Charge postbacks go to the postback_url of the setup transaction

  18. 15
    MerchantOneAPI

    15. Cancel API or Revise API (plan, quantity, shipping)

  19. 16
    OneAPIPayPalPayPal

    16. Cancels or revises the subscription

  20. Merchant

    Revise can return client_action: redirect when PayPal needs the payer to approve the change

Two points are essential to understand before integrating:

  • The transaction you create with POST /rest/payment is a setup transaction with amount: 0. It never becomes completed — once the customer approves, it ends in registered.
  • Every recurring charge is a separate child transaction created by our platform when PayPal reports the billing cycle. Its postback carries original_transaction_id with the setup transaction ID.

Prerequisites​

You should familiarize yourself with PayPal Subscriptions concepts first.

See:

Creation of catalogue products and plans​

When creating a PayPal Subscription through our API, you must already have a PayPal Plan created on PayPal side. Then pass paypal_plan_id when creating the subscription via Payment API.

How PayPal Subscriptions work​

  1. Create a Catalogue Product using PayPal APIs (outside our systems): official documentation.
  2. Create a Plan using PayPal APIs (outside our systems): official documentation.
  3. Create the subscription with POST /rest/payment (payment_type: paypal, recurring: 1, amount: 0, paypal_plan_id).
  4. Redirect the customer to the URL in action_data and let them approve the subscription.
  5. Our systems track follow-up subscription charges and send postbacks to the original transaction postback_url.

Subscription transaction statuses​

A subscription produces one setup transaction plus one child transaction per billing cycle. They use different statuses, so treat them separately in your system.

TransactionStatusMeaning
Setuppending (2)The subscription was created in PayPal and is waiting for the customer's approval.
Setupregistered (9)The customer approved and the subscription is active. This is the final state of the setup transaction — it never becomes completed.
Setupcanceled (5)The subscription was cancelled through the Cancel API, or PayPal reports it as cancelled or expired.
Chargecompleted (3)A billing cycle was charged successfully.
Chargedeclined (6)A billing cycle failed. The subscription itself stays active until PayPal or you cancel it.
Chargerefunded (7)A charge was fully or partially refunded.

APIs for PayPal Subscriptions​

ActionAPI
Create subscriptionPayment API
Cancel subscriptionCancel API
Update plan, quantity, or shippingRevise API

Pass the transaction_id of the setup transaction to Cancel and Revise, not the ID of a charge.

Revise may require a new approval

When the change needs the customer's consent, the Revise API response contains client_action: "redirect" and an action_data.url. Redirect the customer to that URL, otherwise the change is not applied.

Required parameters​

Use the same base parameters as in a standard /rest/payment request, with the additions below.

ParameterRequiredComments
recurringYESMust be 1
amountYESMust be 0. Future amounts come from the PayPal Plan.
paypal_plan_idYESPayPal Plan ID
paypal_subscription_start_timeNOSubscription start time. If omitted, starts immediately. Must follow RFC 3339 format, e.g. 2021-09-29T11:47:25Z.
paypal_subscription_quantityNOQuantity of items in the plan. Only when the plan supports quantities.

Provide first_name, last_name, and email in the billing address for subscription setup. Shipping address parameters are required when the plan is for physical goods.

Create a payment​

POST/rest/payment
payment_typeStringrequired
paypal
recurringIntegerrequired
Must be 1
order_idStringrequired
Any alphanumeric string to identify the Merchant's order
amountFloatrequired
Must be 0. Future billing amounts are defined by the PayPal Plan
currencyStringrequired
3 letter currency code
postback_urlStringrequired
The URL for updates about transaction status are posted
success_urlStringrequired
Where to redirect the user after a successful transaction
error_urlStringrequired
Where to redirect the user after a failed transaction
first_nameStringrequired
Customer's first name (billing address)
last_nameStringrequired
Customer's last name (billing address)
emailStringrequired
Customer's email address
paypal_plan_idStringrequired
PayPal Plan ID created in PayPal (outside our API)
paypal_subscription_start_timeString
Subscription start time in RFC 3339 format, e.g. 2021-09-30T20:47:25Z. Starts immediately when omitted
paypal_subscription_quantityInteger
Quantity within the plan. Only when the plan supports quantities
shipping_costsFloat
Shipping costs when the plan includes shipping
shipping_addressString
Street address. Required with other shipping fields when the plan is for physical goods
shipping_cityString
City of the shipping address
shipping_postal_codeString
Postal code of the shipping address
shipping_stateString
State or region of the shipping address
shipping_countryString
Country code (ISO 3166-1 alpha-2) of the shipping address
shipping_first_nameString
Customer first name for shipping
shipping_last_nameString
Customer last name for shipping
This example creates a PayPal Subscription. See the Create Payment API reference for common and billing address parameters. Include shipping fields when the plan requires physical delivery.
Example RequestHTTP
POST /rest/payment
Authorization: Basic <base64(api_key:api_password)>
Content-Type: application/json
{
"payment_type": "paypal",
"recurring": "1",
"order_id": "123000",
"amount": "0",
"currency": "EUR",
"postback_url": "https://your-postback.url.com",
"success_url": "https://your-success.url.com",
"error_url": "https://your-error.url.com",
"first_name": "Max",
"last_name": "Mustermann",
"email": "max.m@test.com",
"address": "Hellersbergstr. 14",
"city": "Neuss",
"postal_code": "41460",
"state": "NW",
"country": "DE",
"shipping_address": "2211 N First Street",
"shipping_city": "San Jose",
"shipping_postal_code": "95131",
"shipping_state": "CA",
"shipping_country": "US",
"shipping_first_name": "Max",
"shipping_last_name": "Mustermann",
"paypal_plan_id": "P-6UK2193323222643NMFJN66Q",
"shipping_costs": "10",
"paypal_subscription_quantity": 1,
"paypal_subscription_start_time": "2021-09-30T20:47:25Z"
}

Payment API Response​

transaction_idString
ID of the created transaction
payment_typeString
Payment type of the transaction
order_idString
Order ID of the transaction
error_codeInteger
Error code for the response
status_codeInteger
Status code of the transaction
statusString
Status of the transaction (typically pending until the customer approves the subscription)
client_actionString
Indicates the required client-side action. Value: redirect
action_dataObject
Contains url — redirect the customer to this PayPal URL to approve the subscription
paypal_subscription_idString
PayPal Subscription ID created for this transaction
paypal_plan_idString
PayPal Plan ID the subscription is based on
Example ResponseJSON
{
"transaction_id": "dbd3ab13-38f3-49da-9e12-47ef445b0b72",
"payment_type": "paypal",
"order_id": "123000",
"error_code": 0,
"status_code": 2,
"status": "pending",
"client_action": "redirect",
"action_data": {
"url": "https://www.sandbox.paypal.com/webapps/billing/subscriptions?ba_token=BA-0M029980WL960863C"
},
"paypal_subscription_id": "I-B3452HHDSD",
"paypal_plan_id": "P-6UK2193323222643NMFJN66Q"
}

Redirect the customer to the action_data.url. After approval, the customer is redirected to your success_url, and the system starts tracking transactions from this subscription.

Postbacks​

For the full list of payment postback fields, see the Payment API. For refund postbacks, see the Refund API. For retry policy and signature verification, see Postbacks.

Subscription charge postbacks​

Every subscription charge is a separate transaction and produces its own postback, sent to the postback_url of the setup transaction. Each one carries the fields below in addition to the standard payment postback fields.

FieldDescription
transaction_idID of the charge itself. Different on every billing cycle.
original_transaction_idID of the subscription setup transaction. Use it to attach the charge to the right subscription.
amount, currencyAmount charged for this cycle. It comes from the PayPal Plan, not from your original request.
paypal_subscription_idPayPal Subscription ID.
paypal_plan_idPayPal Plan ID in effect for the charge.
statuscompleted, declined, or refunded.

Charges are picked up from PayPal notifications and additionally reconciled with PayPal on a daily basis, so a postback for a cycle can arrive with a delay if a notification is lost. Handle postbacks idempotently, keyed on transaction_id.