> For the complete documentation index, see [llms.txt](https://docs.tiun.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tiun.io/guides/testing/simulate-payments.md).

# Simulate payments

In **sandbox**, checkout runs on Stripe's test mode — no payment method is ever charged, no matter which one you pick. Use this page to complete test purchases, force failures, cancel test subscriptions, and reset state between runs.

Everything here assumes your app is targeting sandbox: `sandbox: true`, your sandbox snippet ID, and `p-test-...` product IDs. See [Sandbox](/reference/generic/sandbox.md) in Reference for how the two environments differ.

***

## Test cards

In the card form, use one of Stripe's test card numbers with **any future expiry date**, **any CVC**, and **any name and postal code**:

| Card number           | Result           |
| --------------------- | ---------------- |
| `4242 4242 4242 4242` | Payment succeeds |
| `4000 0000 0000 0002` | Card is declined |

These cover the common cases; Stripe offers many more numbers for specific brands, countries, and error codes — see the [Stripe testing docs](https://docs.stripe.com/testing) for the full list.

***

## PayPal

Selecting PayPal in sandbox does not open the real PayPal — it redirects to Stripe's **PayPal test setup page**:

1. Choose **PayPal** in the checkout overlay and enter any email and name (they don't need to exist).
2. Continue, and you're redirected to the test setup page.
3. Click **Authorize test setup** to simulate a successful payment, or **Fail test setup** to simulate a failure.

You're then redirected back to your app, and the flow continues exactly like a real PayPal payment would. This is also the easiest way to simulate a failure for a redirect-based payment method.

***

## Apple Pay and Google Pay

Apple Pay and Google Pay appear in sandbox checkout whenever your browser and device support them. You can use your **real wallet** — in test mode the stored card is never charged, so it's safe to complete the flow end to end.

***

## What happens after a successful payment

After any successful test payment the sequence is the same:

1. The overlay shows the success screen.
2. **`userChange`** fires with `event: 'checkout'` — this can happen while the success screen is still visible, before the overlay closes.
3. The event payload's `productAccess` **already contains** the purchased `p-test-...` product ID — access is granted by the time the event fires, for every payment method.

```javascript
tiun.on('userChange', (data) => {
  if (data.event === 'checkout') {
    console.log('Access after purchase:', data.user.productAccess);
  }
});
```

{% hint style="info" %}
Rely on **`userChange`** rather than reading `tiun.user` right after the overlay closes. The event fires exactly when access is in place, so there is nothing to poll or retry. See [Product access](/reference/checkout/product-access.md) for how to gate your UI on it.
{% endhint %}

For all events and payloads, see [SDK events](/sdk/reference/events.md).

***

## Simulating failed payments

Use a declining test card (`4000 0000 0000 0002`) or **Fail test setup** in the PayPal flow. On failure:

* No access is granted — `productAccess` stays unchanged.
* The failure surfaces through the SDK **`error`** event, so you can verify your logging or error UI.
* The user can pick another payment method and retry inside the overlay.

***

## Cancellation and expiration

To test what your app does when access is removed, cancel the test subscription in the **tiun account portal** — the page where end users manage their subscriptions, purchases, transactions, and payment methods:

| Environment | Account portal                                             |
| ----------- | ---------------------------------------------------------- |
| Sandbox     | [account-staging.tiun.io](https://account-staging.tiun.io) |
| Live        | [account.tiun.io](https://account.tiun.io)                 |

Sign in with the same email you used at checkout, find the subscription, and cancel it. Once the cancellation takes effect, the product ID is **removed** from `productAccess` and `userChange` fires — your app should revoke access at that moment, with no extra handling for the expiration itself.

{% hint style="info" %}
This applies to **subscriptions only**. A one-time purchase can't be cancelled and never leaves `productAccess`, so there's no removal to test. To re-run a first-purchase flow for a one-time product, use a fresh test customer — see [Resetting sandbox state](#resetting-sandbox-state) below.
{% endhint %}

***

## Resetting sandbox state

There is no reset button — instead, start over with a **fresh test customer**:

1. Log out in your app (or clear the site data for your dev domain — sessions are per browser).
2. Run checkout again with a **new email address**. Every email is its own test customer, so the new one starts with no purchases and no `productAccess`.

Old test customers remain visible in the sandbox view of the [my.tiun.business dashboard](https://my.tiun.business/); they don't interfere with new ones. This is the way to re-test a first-purchase flow after a previous test email already has access.

***

While testing, keep the [Test flows](/guides/testing/test-flows.md) checklist at hand, and see [Debugging](/guides/testing/debugging.md) if something doesn't behave as expected.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.tiun.io/guides/testing/simulate-payments.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
