---
title: "Create Payment Intent"
method: POST
path: "/iap/payment-intents"
tags: ["Checkout"]
---

# Create Payment Intent

`POST /iap/payment-intents`

Creates a Stripe PaymentIntent for native checkout (Apple Pay / Google Pay payment sheet). Returns a `client_secret` for confirming the payment on the client side and a `checkout_url` for fallback browser checkout.

## Request body

- object
  - `product_id` string, required — The product's `reference_id` (e.g., `com.app.premium`).
  - `external_user_id` string — The user's external ID. Either this or `user_id` is required.
  - `user_id` string — Alias for `external_user_id`. Either this or `external_user_id` is required.
  - `platform` 'ios' | 'android' | 'web' — The platform the purchase is being made from.
  - `stripe_customer_id` string — Existing Stripe customer ID to attach the payment to.
  - `customer_name` string — Optional customer display name to attach to the Stripe customer for this checkout. Also accepts the camelCase alias `customerName`.
  - `customer_email` string — Optional customer email to attach to the Stripe customer for this checkout. Also accepts the camelCase alias `customerEmail`.
  - `checkout_domain` string — Verified custom subdomain for the app's checkout (e.g., `checkout.example.com`). Configure via the dashboard — see the Custom Checkout Domains guide.
  - `checkout_mode` 'native' | 'browser' — Preferred checkout mode. `native` uses the in-app payment sheet; `browser` opens a web checkout page.
  - `storekit_subscription_end` string, date-time — ISO 8601 datetime of the current StoreKit subscription's expiry. Used for Switch & Save migrations to align the trial period.
  - `storekit_original_transaction_id` string — The StoreKit original transaction ID for the subscription being migrated.
  - `external_purchase_token` string — Apple externalPurchaseToken received from StoreKit at the disclosure-sheet confirmation step. When provided, ZeroSettle uses this token for automatic external-purchase reporting in eligible jurisdictions (EU/EEA under the DMA alternative terms, EEA music streaming, and Japan MSCA on iOS 26.4+). Optional — omit for non-Apple platforms or regions where reporting is not required.
  - `ios_version` string — iOS version of the checkout client (e.g., '26.4.1'). Used for JP MSCA regime gating, which requires iOS 26+. Optional.

## Response `200`

Payment intent created successfully

- PaymentIntentResponse — Response from creating a payment intent.
  - `client_secret` string — Stripe PaymentIntent client secret. Use this to confirm the payment on the client side.
  - `payment_intent_id` string — Stripe PaymentIntent ID.
  - `transaction_id` string — ZeroSettle transaction ID. Use this to poll for transaction status.
  - `amount_cents` integer — The charge amount in cents.
  - `currency` string — ISO 4217 currency code (e.g., `usd`).
  - `product_name` string — Display name of the product being purchased.
  - `checkout_url` string — Fallback URL for browser-based checkout.
  - `merchant_country` string — ISO 3166-1 alpha-2 country code of the merchant's Stripe account. Required for Apple Pay `paymentRequest()` country parameter.
  - `original_amount_cents` integer — The original price before any discounts or prorations.
  - `trial_type` 'free_trial' | 'migration', nullable — The type of trial applied to this checkout. `free_trial` for standard free trials, `migration` for Switch & Save aligned trials. Omitted when no trial is active.
  - `trial_end` integer, nullable — Unix timestamp when the trial period ends. Only present when a trial is active.
  - `pending_amount` integer, nullable — The charge amount in cents that will be billed when the trial ends. Only present when a trial is active.
  - `intent_type` 'setup' | 'payment' — Discriminator for which Stripe confirm method the client must call. `setup`: confirms a SetupIntent (free trial — card saved, no immediate charge); `payment`: confirms a PaymentIntent (paid/auth_hold trial upfront charge, or any normal charge). Also returned by `POST /iap/payment-intents/{transaction_id}/finalize/` with the same semantics.

## Other responses

- `400` — Invalid request -- missing or malformed parameters.
- `401` — Invalid or missing API key.
- `502` — Upstream service error (Stripe, Apple).

---

[API](https://skmtc.net/zerosettle/apis/zerosettle-iap-api.md) · [All operations](https://skmtc.net/zerosettle/apis/zerosettle-iap-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/zerosettle/zerosettle-iap-api/versions/1a7aa12f3d5e/schema)
