PayPal - Express Checkout
Audience:
Low-code EngineersSkill Prerequisites:
Actions,Tokens,PayPal business account
Sends the user to PayPal to pay, then brings them back to your page and runs the action list that matches the result. The user logs in and approves the payment on PayPal's site, so card and account details never reach your server.
The action takes a one-time payment, or creates a PayPal subscription (a recurring payments profile) when you set a Recurring Billing Period.
This action is part of the PayPal add-on (DnnSharp.PayPal). The add-on is installed separately and needs the PAYPAL feature in your license. If it isn't licensed, the action fails with a "not licensed" error. If you don't see the PayPal actions, the add-on isn't installed.
Typical Use Cases
- Take a one-time PayPal payment when a user submits an order form
- Sign a user up for a monthly or yearly subscription, with an optional trial and set-up fee
- Save the PayPal transaction ID, so you can look the payment up later with Get Transaction Details
- Save the subscription profile ID, so it can be canceled later with Cancel Express Checkout Subscription
Don't use it to
- Charge a card on your own form. Use Make a payment with Credit Card (Authorize.Net) instead.
- Take payments with Stripe. Use Stripe Checkout instead.
- Take a payment without a user in the browser, for example in a scheduled job. The user has to approve the payment on PayPal.
Related Actions
| Action Name | Description |
|---|---|
| Get Transaction Details | Reads the details of a PayPal transaction by its ID. |
| Cancel Express Checkout Subscription | Cancels a subscription created by this action. |
| PayPal - Parse IPN Request | Turns a PayPal Instant Payment Notification into tokens, for your own IPN handler. |
| Make a payment with Credit Card | Charges a card through Authorize.Net instead. |
| Create Subscription | Creates a recurring payment in Authorize.Net instead. |
| Stripe Checkout | Sends the user to a Stripe payment page instead. |
Input Parameter Reference
| Parameter | Description | Supports Tokens | Default | Required |
|---|---|---|---|---|
| API Username | The API username of your PayPal account's classic API (NVP/SOAP) credentials. Use the sandbox credentials when Go Live is off. | Yes | empty string | Yes |
| API Password | The API password that goes with the username. | Yes | empty string | Yes |
| API Signature | The API signature that goes with the username. | Yes | empty string | Yes |
| Go Live | When on, the payment goes to PayPal's live site and money really changes hands. When off, it goes to the PayPal sandbox (api-3t.sandbox.paypal.com and www.sandbox.paypal.com). | Yes | false | No |
| Payment Description | Only used for subscriptions: it's the subscription description when Recurring Description is empty. It isn't sent for one-time payments. | Yes | empty string | No |
| Item Title | The name of what's being bought, for example Order [OrderId]. For one-time payments, it's shown to the user on PayPal and used as the payment description. | Yes | empty string | No |
| Transaction currency | The currency code: AUD, USD, CAD, GBP, NZD or EUR. You can switch to an expression to use another code that PayPal supports. | Yes | None | Yes |
| Amount | For a one-time payment, the total to pay. For a subscription, the amount of each billing cycle. Use a plain number with a dot for decimals, for example 49.95 or [OrderTotal]. | Yes | empty string | Yes |
| Recurring Billing Period | Leave it on None for a one-time payment. Pick Day, Week, SemiMonth, Month or Year to create a subscription. SemiMonth bills on the 1st and 15th of each month. | Yes | None | No |
| Recurring Billing Frequency | How many periods make up one billing cycle. For example, period Month and frequency 3 bills every three months. The cycle can't be longer than one year, and it must be 1 for SemiMonth. | Yes | empty string | For subscriptions |
| Recurring Billing Cycles | How many times the user is billed. Empty or 0 bills until the subscription is canceled. | Yes | empty string | No |
| Trial Amount | The amount of each trial billing cycle. Use 0 for a free trial. | Yes | empty string | No |
| Recurring Trial Billing Period | The period of the trial, with the same values as Recurring Billing Period. Leave it on None for no trial. Only used for subscriptions. | Yes | None | No |
| Recurring Trial Billing Frequency | How many trial periods make up one trial billing cycle. | Yes | empty string | For trials |
| Recurring Trial Billing Cycles | How many trial cycles there are before normal billing starts. If it's empty, it's 1. | Yes | empty string | No |
| Recurring Initial Amount | A one-time amount charged when the subscription is created, for example a set-up fee. If this payment fails, the subscription is still created. | Yes | empty string | No |
| Recurring Description | The subscription description the user agrees to on PayPal. If it's empty, Payment Description is used, and if that's empty too, the text Description. | Yes | empty string | No |
| On Success | Actions to run when PayPal confirms the payment or creates the subscription. | No | None | No |
| On Error | Actions to run when PayPal returns a failure after the user comes back, for example when the payment is refused. | No | None | No |
| On Pending | Actions to run when the payment is completed but its status is Pending, for example while PayPal reviews it. | No | None | No |
| On Cancel | Actions to run when the user clicks the cancel link on PayPal and comes back without paying. | No | None | No |
| On Subscription Payment Received | Actions to run when PayPal sends an IPN with the subscription status Active. Only shown in Forms and API endpoints. See Subscription notifications. | No | None | No |
| On Subscription Canceled | Actions to run when PayPal sends an IPN with the subscription status Cancelled. Only shown in Forms and API endpoints. | No | None | No |
| On Subscription Suspended | Actions to run when PayPal sends an IPN with the subscription status Suspended, for example after failed payments. Only shown in Forms and API endpoints. | No | None | No |
Output Parameters Reference
Type a name without brackets, for example PayPalTransactionId, and use it later as [PayPalTransactionId]. The tokens are set when the user comes back from PayPal, before On Success, On Error or On Pending run. They aren't available to actions outside those lists.
| Parameter | Description |
|---|---|
| Output PayPal Transaction Id Token Name | The PayPal transaction ID of a one-time payment. Save it if you may need to look up or refund the payment later. Empty for subscriptions. |
| Output PayPal Response Payment Type Token Name | The payment type PayPal returns, for example instant or echeck. |
| Output PayPal Response Payment Status Token Name | The payment status, for example Completed or Pending. |
| Output PayPal Response Pending Reason Token Name | Why the payment is pending, when it is. |
| Output PayPal Response Reason Code Token Name | PayPal's reason code for the payment status. |
| Output recurring Profile Id Token Name | The ID of the new subscription (recurring payments profile), for example I-DF3S35GWL16Y. Save it: you need it to cancel the subscription. |
| Output recurring Profile Status Token Name | The subscription status: ActiveProfile when it's active, or PendingProfile while PayPal is still creating it. |
| Output Error Code Token Name | PayPal's error code, when there's an error. |
| Output Short Message Token Name | PayPal's short error message. |
| Output recurring Long Message Token Name | PayPal's detailed error message. It's used for one-time payments too. |
How the payment works
The action uses PayPal's classic Express Checkout NVP API (version 109.0), with the API username, password and signature you provide. PayPal considers this API legacy, and new PayPal accounts may not get classic API credentials.
- The user clicks the button. The action calls
SetExpressCheckoutwith the amount, or with a billing agreement for a subscription. It saves the action and the current tokens on the server, encrypted, and redirects the user to PayPal. It's a final action, so actions after it in the same list don't run. - The user approves on PayPal. For one-time payments, the action asks PayPal to open its billing page, so the user can pay by card without a PayPal account, if your PayPal account allows guest checkout.
- PayPal sends the user back. The return and cancel addresses point to
/DesktopModules/DnnSharp/Common/WebHandlers/PayPalExpressCheckoutHandler.ashxon your site. You don't set them yourself. - The payment is completed. The handler calls
GetExpressCheckoutDetails, and thenDoExpressCheckoutPaymentfor a one-time payment orCreateRecurringPaymentsProfilefor a subscription. It restores the saved tokens, adds the output tokens and runs one list:
| PayPal result | List that runs |
|---|---|
ACK is Failure | On Error |
Payment status is Pending | On Pending |
ACK is Success | On Success |
| The user clicked cancel on PayPal | On Cancel |
In Forms, the result of the list, such as a message, is shown on the form's page. Then the user is back on the page where the action ran.
Subscription notifications
PayPal reports later subscription events, such as payments, suspensions and cancellations, with Instant Payment Notifications (IPN). The action gives PayPal this notify address, and its help text says to also set it as the IPN address in your PayPal account's profile:
https://your-site/DesktopModules/DnnSharp/Common/WebHandlers/PayPalExpressCheckoutHandler.ashx/Ipn?DnnSharpPaymentType=Recurring
When an IPN arrives, its recurring_payment_id is matched to the subscription, and the list for its profile_status runs: Active runs On Subscription Payment Received, Suspended runs On Subscription Suspended and Cancelled runs On Subscription Canceled. The lists get the tokens saved at checkout, including the output tokens. The action doesn't add the IPN fields as tokens.
Keep these limits in mind:
- The IPN isn't verified with PayPal. The handler doesn't post the message back to PayPal to confirm it's genuine. Anyone who knows a subscription's profile ID can send a fake notification. Don't grant anything valuable, such as extending access, based only on these lists. Confirm with PayPal first, for example with Get Transaction Details.
- The subscription data is kept for 7 days. After that, IPNs for the subscription don't run any list. For subscriptions that bill monthly or yearly, handle IPNs yourself in an API endpoint with PayPal - Parse IPN Request.
Activeisn't only a payment. The status isActivein most notifications for a working subscription, so On Subscription Payment Received may also run when the subscription is created.
Considerations
- Test with the sandbox first. With Go Live off, use the API credentials of a PayPal sandbox business account and pay with a sandbox personal account. Turn Go Live on, with your live credentials, only when you're ready to take real payments.
- Keep the credentials out of the action. The API password is a password field, but the signature isn't, and both are stored with the action. Use tokens, for example from a setting, so the keys aren't typed into each action.
- Errors before the redirect don't run On Error. Wrong credentials, a missing amount or a connection problem when the button is clicked make the action fail with an exception that starts with
Error:. On Error only runs for failures after the user comes back from PayPal. To handle the others, put the action inside Execute Actions and use its On Error. - Do the work in the lists. Save the order, send the receipt or grant access in On Success. Nothing after the action in the same list runs.
- Place it at the top level. In Forms, put the action directly in the button's action list, not inside another action's list. The action lists are looked up again when the user comes back, and that only works for top-level actions.
- The user may not come back. If the user closes the browser on PayPal, no list runs. The saved data expires after 7 days.
- A warning isn't a success. If PayPal answers with
SuccessWithWarning, no list runs, even though the payment went through. - Amount format. The amount is sent as typed, after tokens are replaced. Round it to two decimals and don't include a currency symbol or thousands separators.
- Where it works. The action needs a page to return to and a browser to redirect, so use it on a form or listing button. It doesn't work in scheduled jobs.
Examples
To understand how to use the below examples, please see Running Examples.
1. Take a one-time payment in the sandbox
This action sends the user to the PayPal sandbox to pay the order total. The API credentials come from the PayPalApiUser, PayPalApiPassword and PayPalApiSignature tokens. When the payment is confirmed, the transaction ID is saved with the order and shown to the user.
{
"Title": "PayPal - Express Checkout",
"ActionType": "PayWithPaypalExpressCheckout",
"Description": "Pay the order total with PayPal",
"Parameters": {
"PayPalExpressId": "[PayPalApiUser]",
"PayPalExpressPassword": "[PayPalApiPassword]",
"PayPalExpressSignature": "[PayPalApiSignature]",
"PayPalExpressLiveMode": false,
"PayPalExpressItemTitle": "Order [OrderId]",
"PayPalExpressCurrency": "USD",
"PayPalExpressAmount": "[OrderTotal]",
"PayPalExpressRecurringBillingPeriod": "None",
"PayPalResponseTransactionIDTokenName": "PayPalTransactionId",
"PayPalResponsePaymentStatus": "PayPalPaymentStatus",
"PayPalResponsePendingReason": "PayPalPendingReason",
"PayPalResponseLongMessage": "PayPalError",
"OnSuccess": [
{
"Title": "Run SQL Query",
"ActionType": "RunSql",
"Parameters": {
"SqlQuery": "UPDATE Orders SET Status = 'Paid', PayPalTransactionId = @TransactionId WHERE OrderId = @OrderId",
"BindTokens": [
{
"name": "TransactionId",
"value": "[PayPalTransactionId]"
},
{
"name": "OrderId",
"value": "[OrderId]"
}
]
}
},
{
"Title": "Display Message",
"ActionType": "ShowMessage",
"Parameters": {
"Message": "<p>Thank you! Your payment was received. Transaction ID: [PayPalTransactionId]</p>"
}
}
],
"OnPending": [
{
"Title": "Display Message",
"ActionType": "ShowMessage",
"Parameters": {
"Message": "<p>Your payment is pending ([PayPalPendingReason]). We'll email you when it's confirmed.</p>"
}
}
],
"OnError": [
{
"Title": "Display Error Message",
"ActionType": "ShowError",
"Parameters": {
"Message": "<p>We couldn't complete your payment: [PayPalError]</p>"
}
}
],
"OnCancel": [
{
"Title": "Display Message",
"ActionType": "ShowMessage",
"Parameters": {
"Message": "<p>You canceled the payment. Your order wasn't placed.</p>"
}
}
]
}
}
2. Create a monthly subscription with a free trial
This action creates a subscription that bills [PlanPrice] every month until it's canceled, after one free month. It runs in production, so use it only with real credentials. The profile ID is saved with the user, so the subscription can be canceled later.
{
"Title": "PayPal - Express Checkout",
"ActionType": "PayWithPaypalExpressCheckout",
"Description": "Subscribe to the monthly plan",
"Parameters": {
"PayPalExpressId": "[PayPalApiUser]",
"PayPalExpressPassword": "[PayPalApiPassword]",
"PayPalExpressSignature": "[PayPalApiSignature]",
"PayPalExpressLiveMode": true,
"PayPalExpressCurrency": "USD",
"PayPalExpressAmount": "[PlanPrice]",
"PayPalExpressRecurringBillingPeriod": "Month",
"PayPalExpressRecurringBillingFrequency": "1",
"PayPalExpressTrialAmount": "0",
"PayPalExpressRecurringTrialBillingPeriod": "Month",
"PayPalExpressRecurringTrialBillingFrequency": "1",
"PayPalExpressRecurringTrialBillingCycles": "1",
"PayPalExpressRecurringDescription": "Monthly plan, [PlanPrice] USD per month after a free first month",
"PayPalResponserecurringProfileId": "PayPalProfileId",
"PayPalResponserecurringProfileStatus": "PayPalProfileStatus",
"PayPalResponseLongMessage": "PayPalError",
"OnSuccess": [
{
"Title": "Run SQL Query",
"ActionType": "RunSql",
"Parameters": {
"SqlQuery": "UPDATE Members SET PayPalProfileId = @ProfileId, ProfileStatus = @ProfileStatus WHERE UserId = @UserId",
"BindTokens": [
{
"name": "ProfileId",
"value": "[PayPalProfileId]"
},
{
"name": "ProfileStatus",
"value": "[PayPalProfileStatus]"
},
{
"name": "UserId",
"value": "[User:UserId]"
}
]
}
}
],
"OnError": [
{
"Title": "Display Error Message",
"ActionType": "ShowError",
"Parameters": {
"Message": "<p>We couldn't create your subscription: [PayPalError]</p>"
}
}
]
}
}
Revised 09/27/2026