Stripe Checkout
Audience:
Low-code EngineersSkill Prerequisites:
Actions,Tokens,Lists,Connectors,Stripe account
Sends the user to a Stripe-hosted Checkout page to pay by card. The items to pay for come from a list. When the payment is complete, Stripe calls your site with a webhook, and the action runs its On Webhook Call actions to fulfill the order.
The action takes a one-time payment (Direct Payment), or starts a subscription to one or more Stripe plans (Subscription). The card is entered on Stripe's page, so card details never reach your server, and Stripe handles Strong Customer Authentication (SCA), such as 3D Secure.
This action is part of the Stripe add-on (DnnSharp.Stripe). The add-on is installed separately and needs the STRIPE feature in your license. If it isn't licensed, the action fails with a "not licensed" error. If you don't see the Stripe actions, the add-on isn't installed.
Typical Use Cases
- Take a card payment for the items in a shopping cart
- Sell a single product or service from a form or listing button
- Sign a user up for a monthly or yearly plan, with an optional free trial
- Take a payment on behalf of a connected account (Stripe Connect) and keep an application fee
Don't use it to
- Charge a card on your own form. Use Stripe Payment with Token (not SCA compliant) only if you can't use Checkout.
- Subscribe an existing Stripe customer without sending them to Stripe. Use Stripe Create Subscription instead.
- Take payments with PayPal. Use PayPal - Express Checkout instead.
- Take a payment without a user in the browser, for example in a scheduled job or an API endpoint.
Related Actions
| Action Name | Description |
|---|---|
| Add Stripe Connector | Creates the Stripe connector that holds your secret key. |
| Add List Entry | Adds the items to pay for to a list. |
| Stripe Create Plan | Creates a plan you can sell with the Subscription payment type. |
| Stripe Create Customer | Creates a Stripe customer, whose ID you can pass in Customer ID. |
| Stripe Get Event Details | Handles other Stripe webhook events in an API endpoint. |
| Parse JSON Into Tokens | Reads fields from [Stripe:SessionRawResponse]. |
Input Parameter Reference
| Parameter | Description | Supports Tokens | Default | Required |
|---|---|---|---|---|
| API Connector | The Stripe connector with your secret key. See Add Stripe Connector. When using an expression, the value must be the connector ID. | Yes | None | Yes |
| Publishable API Key | Your Stripe publishable key, for example pk_test_... or pk_live_.... It's used in the browser to open the Checkout page. It must belong to the same Stripe account, and the same test or live mode, as the connector's secret key. | Yes | empty string | Yes |
| List Name | The name of the list with the items to pay for, for example CartList. The list needs specific columns. See The items list. | Yes | empty string | Yes |
| Reference Id | Your own ID for this checkout, for example an order or cart ID. It's sent to Stripe as the session's client_reference_id. If it's empty, the action generates a GUID. If it has a value that becomes empty after tokens are replaced, the action fails. | Yes | empty string | No |
| Customer Email | Pre-fills the email on the Checkout page. Don't use it together with Customer ID. | Yes | empty string | No |
| Payment Type | Direct Payment for a one-time payment, or Subscription to subscribe the user to plans. | No | Direct Payment | No |
| Allow Promotion Codes | When on, the Checkout page shows a field for Stripe promotion codes. | No | false | No |
| Locale | The language of the Checkout page, for example fr or de. Stripe lists the supported values. auto lets Stripe use the browser's language. | Yes | en | No |
| Payment Description | Direct Payment only. A description of the payment, shown in your Stripe dashboard. | Yes | empty string | No |
| Customer ID | The ID of an existing Stripe customer, for example cus_.... The payment is linked to that customer. Shown for Direct Payment only. | Yes | empty string | No |
| Trial Period Days | Subscription only. The number of free days before the first charge. Empty or 0 charges right away. | Yes | empty string | No |
| Transfer Destination | Direct Payment only. The ID of a connected account (acct_...) that receives the funds. The payment is also made on behalf of that account. See Stripe Add Seller. | Yes | empty string | No |
| Application Fee Amount | Direct Payment only, with Transfer Destination. The fee your platform keeps, as a whole number in the currency's smallest unit, for example 250 for 2.50 USD. Note that this is different from the list's Amount column. | Yes | empty string | No |
| Success Url | Where Stripe sends the user after paying, for example a thank-you page. A relative URL, such as /thank-you, is added to your site's address. If it's empty, the user comes back to the current page. | Yes | empty string | Yes |
| Cancel Url | Where Stripe sends the user if they cancel. It works like Success Url. | Yes | empty string | Yes |
| On Webhook Call | Actions to run when Stripe reports that the checkout is complete. Do the fulfillment here. See How the payment works. | No | None | No |
| On Error | Actions to run when Stripe rejects the request to create the checkout. The error is in [Stripe:ErrorMessage]. | No | None | No |
Output Parameters Reference
The action has no output parameters. It sets these tokens:
| Token | Description |
|---|---|
[Stripe:SessionId] | The ID of the Checkout Session, for example cs_test_.... Available in On Webhook Call. |
[Stripe:SessionRawResponse] | The Checkout Session that Stripe sent in the webhook, as JSON. Available in On Webhook Call. Read its fields, such as client_reference_id, customer or payment_intent, with Parse JSON Into Tokens. |
[Stripe:ErrorMessage] | Stripe's error message. Available in On Error. |
The items list
Build the list before this action runs, for example with Add List Entry or Create List from SQL. Each row is one line on the Checkout page. If the list is empty, the action fails with You need to select some items before proceeding to checkout.
For Direct Payment, each row needs these columns:
| Column | Description |
|---|---|
Name | The item's name, shown on the Checkout page. Required. |
Description | The item's description. The column must exist. |
Amount | The price of one item in the currency's main unit, with a dot for decimals, for example 19.99. The action converts it to cents. Don't use cents here. |
Currency | A three-letter currency code, for example usd or eur. Use the same currency on every row. |
Quantity | A whole number, for example 1. |
Images | Optional. One or more image URLs, separated by |. |
For Subscription, each row needs these columns:
| Column | Description |
|---|---|
PlanId | The ID of a Stripe plan, for example from Stripe Create Plan. |
Quantity | Optional. A whole number. If the column is missing, it's 1. |
If a required column is missing, the action fails with an error.
How the payment works
- The user clicks the button. The action creates a Checkout Session in Stripe, for card payments. It saves the action and the current tokens on the server, encrypted, for 24 hours. Then it redirects the user to Stripe. It's a final action, so actions after it in the same list don't run.
- The user pays on Stripe. Stripe sends them to Success Url, or to Cancel Url if they cancel.
- Stripe calls your webhook. Stripe sends the
checkout.session.completedevent to your site. The add-on checks the event's signature, finds the saved data by session ID and Reference Id, and runs On Webhook Call with the saved tokens and the twoStripe:Session...tokens.
You set up the webhook once per site:
-
In the Stripe dashboard, add a webhook endpoint with this URL, and pick only the
checkout.session.completedevent:https://your-site/DesktopModules/DnnSharp/Stripe/API/Payment/FullfillPayment -
Copy the endpoint's signing secret (
whsec_...) and add it to theappSettingssection of your site'sweb.config. Add the portal ID to the key name to use a different secret for each portal:<add key="DnnSharpStripeApiSigningSecretKey_0" value="whsec_..." />A key without the portal ID,
DnnSharpStripeApiSigningSecretKey, is used for any portal that doesn't have its own.
If the signature is missing or doesn't match, or the secret isn't set, the webhook fails and On Webhook Call doesn't run. Other event types are answered with an error, so don't send them to this URL.
Considerations
- Fulfill in On Webhook Call, not on the success page. Anyone can open the Success Url without paying, and some users close the browser before they get there. The webhook is the only reliable signal.
- On Webhook Call runs without the user. It runs in a request from Stripe, not in the user's browser, so actions that show a message or redirect have no effect there. Save the order, send the email or grant access instead.
- Handle repeats. Stripe may send the same event more than once, and the saved data isn't removed after the list runs. Check
[Stripe:SessionId]against your records before you fulfill an order again. - The saved data expires after 24 hours. If the webhook arrives later, On Webhook Call doesn't run.
- Stripe.js must be on the page. The action opens Checkout with Stripe.js in the browser, but doesn't load it. The Stripe Credit Card form field loads it. Otherwise, make sure the page includes
https://js.stripe.com/v3/. If Stripe.js is missing or the redirect fails, nothing happens on the page and the error only appears in the browser console. - On Error only covers Stripe errors. It runs when Stripe rejects the request, for example because of a wrong key or plan ID. A missing connector, list or column, or an invalid amount, fails the action with an exception instead. To handle those, put the action inside Execute Actions and use its On Error.
- Customer Email or Customer ID, not both. Stripe rejects a checkout that has both.
- Zero-decimal currencies. The action always multiplies Amount by 100. For currencies without cents, such as
jpy, this charges 100 times too much. Test these currencies carefully. - Amount format. Round amounts to two decimals and don't include a currency symbol or thousands separators. Extra decimals are cut off, not rounded.
- Test first. Use your test keys (
sk_test_...andpk_test_...) and a test-mode webhook, which has its own signing secret. Switch the connector, the publishable key and the webhook secret together when you go live. - Keep the keys out of the action. The secret key lives in the connector. The publishable key isn't secret, but using a token for it makes switching to live easier.
- Where it works. Forms and listings only, on a button that runs in the browser.
Examples
To understand how to use the below examples, please see Running Examples.
1. Pay for an order
These actions add the order to a list and send the user to Stripe to pay. When Stripe confirms the payment, the order is marked as paid. Replace the connector ID with your Stripe connector.
[
{
"Title": "Add List Entry",
"ActionType": "CreateEntity",
"Description": "Add the order to the checkout list",
"Parameters": {
"EntityName": "CartList",
"EntityProps": [
{
"name": "Name",
"value": "Order [OrderId]"
},
{
"name": "Description",
"value": "[ProductName]"
},
{
"name": "Amount",
"value": "[OrderTotal]"
},
{
"name": "Currency",
"value": "usd"
},
{
"name": "Quantity",
"value": "1"
}
]
}
},
{
"Title": "Stripe Checkout",
"ActionType": "StripeCheckoutRedirect",
"Description": "Send the user to Stripe to pay",
"Parameters": {
"Credentials": {
"Entry": "00000000-0000-0000-0000-000000000000"
},
"PublishableKey": "[StripePublishableKey]",
"EntityName": "CartList",
"ClientReferenceId": "[OrderId]",
"CustomerEmail": "[Email]",
"PaymentType": "OneTime",
"PaymentDescription": "Order [OrderId]",
"SuccessUrl": "/order-complete",
"CancelUrl": "/cart",
"OnWebhookCall": [
{
"Title": "Run SQL Query",
"ActionType": "RunSql",
"Parameters": {
"SqlQuery": "UPDATE Orders SET Status = 'Paid', StripeSessionId = @SessionId WHERE OrderId = @OrderId AND Status <> 'Paid'",
"BindTokens": [
{
"name": "SessionId",
"value": "[Stripe:SessionId]"
},
{
"name": "OrderId",
"value": "[OrderId]"
}
]
}
}
],
"OnError": [
{
"Title": "Display Error Message",
"ActionType": "ShowError",
"Parameters": {
"Message": "<p>We couldn't start the payment: [Stripe:ErrorMessage]</p>"
}
}
]
}
}
]
2. Start a subscription with a free trial
These actions subscribe the user to the plan in [PlanId], with 14 free days. When Stripe confirms, the subscription ID is read from the session and saved with the user.
[
{
"Title": "Add List Entry",
"ActionType": "CreateEntity",
"Description": "Add the plan to the checkout list",
"Parameters": {
"EntityName": "PlansList",
"EntityProps": [
{
"name": "PlanId",
"value": "[PlanId]"
}
]
}
},
{
"Title": "Stripe Checkout",
"ActionType": "StripeCheckoutRedirect",
"Description": "Subscribe the user to the plan",
"Parameters": {
"Credentials": {
"Entry": "00000000-0000-0000-0000-000000000000"
},
"PublishableKey": "[StripePublishableKey]",
"EntityName": "PlansList",
"ClientReferenceId": "[User:UserId]",
"CustomerEmail": "[User:Email]",
"PaymentType": "Subscription",
"TrialPeriodDays": "14",
"SuccessUrl": "/welcome",
"CancelUrl": "/pricing",
"OnWebhookCall": [
{
"Title": "Parse JSON Into Tokens",
"ActionType": "ParseJsonIntoTokens",
"Parameters": {
"JsonString": "[Stripe:SessionRawResponse]",
"BaseTokenName": "Session"
}
},
{
"Title": "Run SQL Query",
"ActionType": "RunSql",
"Parameters": {
"SqlQuery": "UPDATE Members SET StripeSubscriptionId = @SubscriptionId, StripeCustomerId = @CustomerId WHERE UserId = @UserId",
"BindTokens": [
{
"name": "SubscriptionId",
"value": "[Session:subscription]"
},
{
"name": "CustomerId",
"value": "[Session:customer]"
},
{
"name": "UserId",
"value": "[User:UserId]"
}
]
}
}
]
}
}
]
Revised 09/27/2026