DrPlugin

Stripe payments

Updated November 3, 2026

Connect ReleaseCart to Stripe, set up the webhook that completes orders, and test payments before you take real money.

How Stripe payments work

ReleaseCart takes one-time payments through Stripe. When the buyer clicks Pay now, ReleaseCart creates a pending order and a Stripe PaymentIntent for the order total. The checkout then shows the Stripe Payment Element, a payment form that Stripe serves into the page. ReleaseCart asks Stripe for automatic payment methods, so the form offers the methods you turn on in your Stripe Dashboard.

The checkout with the Stripe Payment Element: card number, expiry date, security code and country fields above Pay now

Card details go from the buyer's browser to Stripe and never reach your server. Stripe handles Strong Customer Authentication and 3D Secure, including any extra check the buyer's bank asks for.

ReleaseCart marks the order Completed only when Stripe's webhook confirms the payment. Checkout and billing fields shows the steps from the buyer's side.

Subscriptions are a ReleaseCart Pro feature: Subscriptions covers them. ReleaseCart Pro also adds PayPal payments.

What ReleaseCart sends to Stripe

Before each payment, ReleaseCart saves the buyer as a Stripe customer. Then it creates the PaymentIntent.

Stripe object What ReleaseCart sends
Customer The buyer's email, the first and last name typed at checkout, and the billing address and country when checkout collects them. When the buyer typed no name, ReleaseCart sends the name on the customer record, if there is one. The customer also carries ReleaseCart's own customer id.
PaymentIntent The order total and currency, the customer, ReleaseCart's order identifiers and a description such as "Order 1042: Acme Forms (Agency)". The description names each product with its price tier, with "2 x" in front of a quantity over one.

ReleaseCart keeps one Stripe customer per buyer in each Stripe mode. A later purchase by the same buyer updates that customer with the details from the new checkout. If the customer was deleted in Stripe, or the keys now belong to another Stripe account, ReleaseCart creates a new one. Each order records the Stripe customer that paid it.

ReleaseCart emails its own receipt. If Successful payments is on under Settings → Business → Customer emails in your Stripe Dashboard, the buyer may also get Stripe's own receipt by email. Turn that setting off if you want buyers to get only ReleaseCart's receipt.

When you erase a buyer's personal data, ReleaseCart removes its link to the Stripe customer, so the next purchase creates a new one. The customer stays in your Stripe account. Delete it in the Stripe Dashboard if you need to.

Developers can change the description with the releasecart_stripe_payment_description filter. It receives the description and the order. ReleaseCart ignores an empty value and cuts a description to 500 characters.

add_filter(
	'releasecart_stripe_payment_description',
	function ( $description, $order ) {
		return sprintf( 'Acme Store order %d', $order->id() );
	},
	10,
	2
);

Connect your Stripe account

Open ReleaseCart → Settings → Payments. You need the manage_releasecart_settings capability, which administrators have. With ReleaseCart Pro, the tab splits into Stripe and PayPal sub-tabs.

  1. In your Stripe Dashboard, switch to test mode and open the API keys page. Stripe's API keys guide shows where it is.
  2. Copy the publishable key into Test publishable key and the secret key into Test secret key.
  3. Leave Stripe mode on Test.
  4. Click Save changes.
  5. Add the test webhook endpoint and paste its signing secret into Test webhook signing secret. Set up the webhook has the steps.

Each mode has its own keys, so you can set up and test everything in test mode before you enter live keys. Go live lists the steps for the switch.

The Stripe test mode keys: the publishable key and two saved secrets shown masked, each with a Clear button

Field What it holds
Stripe mode Test (default) or Live. Checkout, refunds and webhook checks use only the keys of this mode.
Test publishable key, Live publishable key The publishable key. It starts with pk_test_ or pk_live_.
Test secret key, Live secret key The secret key, which starts with sk_, or a restricted key, which starts with rk_. Stored encrypted.
Test webhook signing secret, Live webhook signing secret The signing secret of the webhook endpoint for that mode. It starts with whsec_. Stored encrypted.

Checkout works only when the current mode has both a publishable key and a secret key.

After you save a secret, its field shows only a mask with the last four characters, and the label adds "(enter a new value to replace)". To change it, type the new value and save. Clear removes the secret when you save, and Undo brings it back before then.

Use a restricted key

You can paste a restricted key into the secret key field instead of the full secret key. The free plugin creates customers, PaymentIntents and refunds at Stripe, so give the key Write access to Customers, Payment Intents and Charges and Refunds. Write access includes read access, which ReleaseCart uses to look up refunds made in the Stripe Dashboard. Also give it Read access to Accounts, in the Connect group: ReleaseCart reads the account's country with it when you save the key (see Stripe accounts in India). ReleaseCart Pro subscriptions call more Stripe APIs, such as products and subscriptions, so a restricted key needs more permissions there. Stripe's API keys guide explains how to create one.

When SECURE_AUTH_KEY changes

ReleaseCart encrypts the secret keys and signing secrets with a key built from SECURE_AUTH_KEY in wp-config.php. If that constant changes, for example after a move to a new host, ReleaseCart can no longer read the saved secrets. Their fields then show as empty and checkout stops until you enter the secrets again. Without SECURE_AUTH_KEY, ReleaseCart cannot save a secret at all.

Stripe accounts in India

Stripe asks more of a business account registered in India. Each payment by a card issued outside India must carry the buyer's name, a billing address with the country, and a description. Stripe must also turn on international payments for the account. Stripe's guide for Indian accounts explains the steps.

When you save a secret key, ReleaseCart asks Stripe for the account's country. While the account of the current Stripe mode is in India, checkout requires the first name, the last name, the country and the billing address, whatever the checkout settings say. The descriptions of those settings give the reason. ReleaseCart sends the name, the address and a description with each payment, as What ReleaseCart sends to Stripe shows. A store with a Stripe account in another country keeps its own checkout settings.

The requirement covers the whole checkout form, so a buyer who pays with PayPal in ReleaseCart Pro fills in the same fields.

ReleaseCart reads the country only when you save a secret key under ReleaseCart → Settings → Payments. Keys stored another way, for example by a migration, are not checked until you save the secret key there. After you connect another Stripe account, save its secret key again. Clearing a secret key also clears the country ReleaseCart read for that mode.

If ReleaseCart cannot read the country, the secret key's description says "ReleaseCart could not read this Stripe account's country.", and checkout keeps your settings. A restricted key needs Read access to Accounts for this check. Give the key that permission, then paste it into the field again and save. A save that leaves the field empty does not repeat the check.

Set up the webhook

ReleaseCart completes an order only when Stripe's webhook confirms the payment. Without the webhook, Stripe takes the money but the order stays Pending in ReleaseCart.

  1. In your Stripe Dashboard, add a webhook endpoint. Stripe's webhooks guide shows the current screens.
  2. Set the endpoint URL to https://yourstore.example/wp-json/releasecart/v1/webhooks/stripe, with your own domain.
  3. For the API version, choose a version whose name ends in .endive, such as 2026-10-28.endive, or an older one. ReleaseCart reads events in those versions.
  4. Select the events payment_intent.succeeded, payment_intent.payment_failed and charge.refunded.
  5. Save the endpoint and copy its signing secret.
  6. Paste it into the signing secret field for the same mode under ReleaseCart → Settings → Payments, then click Save changes.

ReleaseCart sends Stripe API version 2026-10-28.endive with every request it makes, so your account's default version does not change what it sends. Webhook events are different: Stripe formats them with the API version of the endpoint they go to.

Stripe releases a new major API version twice a year. The Dashboard offers your account's default version and the latest one, so once both are newer than endive, it offers no version that ReleaseCart reads. This happens, for example, on a Stripe account opened after the next major release. Then create the endpoint through Stripe's API, with the URL from step 2, the events from step 4 and api_version set to 2026-10-28.endive (Create a webhook endpoint in Stripe's API reference), or wait for a ReleaseCart update that reads the newer version.

Test mode and live mode each need their own endpoint, and each endpoint has its own signing secret. ReleaseCart checks every webhook against the signing secret of the current Stripe mode, so a live event fails the check while the mode is Test.

ReleaseCart Pro subscriptions need more events on the same endpoint. Subscriptions lists them.

Event What ReleaseCart does
payment_intent.succeeded Marks the order Completed. ReleaseCart then issues the license keys and queues the receipt email.
payment_intent.payment_failed Marks a pending order Failed.
charge.refunded Records on the order any refund made in the Stripe Dashboard.

Stripe can send the same event more than once. ReleaseCart records each event id and acts on it once, so a repeat never completes an order twice. An event ReleaseCart does not handle gets a success reply and is ignored. If handling an event fails, ReleaseCart replies with an error so that Stripe sends it again.

Refunds

You can refund from ReleaseCart or from the Stripe Dashboard. Either way, the refund is recorded on the order once.

  • From ReleaseCart, open the order under ReleaseCart → Orders and click Refund. ReleaseCart asks Stripe to refund the payment, then records the refund. Refund an order walks through the screen.
  • From the Stripe Dashboard, Stripe sends charge.refunded, and ReleaseCart records any refunded amount it has not recorded yet. Each refund is recorded with its Stripe refund id. When the event does not list the charge's refunds, as on newer API versions, ReleaseCart reads them from Stripe. If that read fails, it records the amount without the id.

A refund started in ReleaseCart is not counted again when Stripe's webhook reports it. Refunds use the secret key of the current Stripe mode, so a refund of a live order fails while the mode is Test.

Test payments

With Stripe mode on Test and the test keys saved, checkout uses Stripe's test environment and no real money moves. Stripe publishes test card numbers in its testing guide. Use any future expiry date and any three-digit CVC.

Card number Result
4242 4242 4242 4242 A successful payment.
4000 0025 0000 3155 Asks for 3D Secure authentication.
4000 0000 0000 9995 A declined payment.

A site on your own computer cannot receive webhooks from Stripe over the internet. The Stripe CLI can forward test events to it:

stripe listen --forward-to https://yourstore.example/wp-json/releasecart/v1/webhooks/stripe

When it starts, the CLI prints a signing secret that starts with whsec_. Paste that into Test webhook signing secret while you test with the CLI. The CLI sends events in your account's default API version. ReleaseCart reads them when that version ends in .endive or is older.

Go live

  1. Copy the live publishable key and secret key from your Stripe Dashboard into Live publishable key and Live secret key.
  2. Add a live webhook endpoint with the same URL and events, and paste its signing secret into Live webhook signing secret.
  3. Set Stripe mode to Live.
  4. Click Save changes.

Common questions

A buyer paid, but the order is still pending. What is wrong?

The webhook did not reach ReleaseCart, or it failed the signature check. Check that the endpoint URL is right, that the endpoint sends payment_intent.succeeded, and that the signing secret in ReleaseCart belongs to that endpoint and to the current Stripe mode. The endpoint's event log in the Stripe Dashboard shows each delivery, the status code your site returned and the reply.

Why does the webhook fail with an invalid signature?

ReleaseCart replies with status 400 and "Webhook signature verification failed." The signing secret usually belongs to another endpoint or the other mode. The server clock can also be the cause: ReleaseCart refuses a signature whose timestamp is more than five minutes from the server time.

Why does the checkout say it is not available?

The current Stripe mode has no publishable key or no secret key. This happens when Stripe mode is Test but only the live keys are filled in, or the other way round. It also happens after SECURE_AUTH_KEY changes, until you enter the secrets again.

Does ReleaseCart ever see card numbers?

No. The Stripe Payment Element sends card details from the buyer's browser to Stripe. Your server sees only the PaymentIntent id and its status.