Skip to content

Payment Intents

The Payment Intents API manages the lifecycle of Stripe PaymentIntents for ecommerce checkout.

Payment Intent Flow

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Client    │     │ TransactCore│     │   Stripe    │
└──────┬──────┘     └──────┬──────┘     └──────┬──────┘
       │                   │                   │
       │ POST /payment-    │                   │
       │ intents           │                   │
       │──────────────────>│                   │
       │                   │ Create Intent     │
       │                   │──────────────────>│
       │                   │   client_secret   │
       │                   │<──────────────────│
       │   client_secret   │                   │
       │<──────────────────│                   │
       │                   │                   │
       │ stripe.confirm    │                   │
       │ Payment()         │                   │
       │─────────────────────────────────────>│
       │          Payment result              │
       │<─────────────────────────────────────│
       │                   │                   │
       │ GET /payment-     │                   │
       │ intents/{id}      │                   │
       │──────────────────>│ Sync status       │
       │                   │──────────────────>│
       │   status:         │                   │
       │   succeeded       │                   │
       │<──────────────────│                   │

Create Payment Intent

Create a Stripe PaymentIntent for processing a payment.

POST /v1/ecommerce/payment-intents

Authentication: OAuth Token

Scope: transactcore:payment-intents.create

Rate Limit: 100/min per IP

Request Body

json
{
  "organization_id": "org_00000k1L2m3N4o5",
  "amount": 3420,
  "currency": "usd",
  "metadata": {
    "order_type": "takeout",
    "quote_token_id": "qt_abc123"
  }
}

Request Fields

FieldTypeRequiredDescription
organization_idstringYes*Organization hashkey — TransactCore resolves the connected Stripe account for this organization
connected_account_idstringYes*Connected account hashkey — use this if you already know the specific account
amountintegerYesAmount in cents (e.g., 3420 = $34.20). Minimum: 50 ($0.50)
currencystringNoThree-letter ISO currency code (default: usd)
metadataobjectNoArbitrary key-value pairs attached to the Stripe PaymentIntent

*One of organization_id or connected_account_id is required.

Response

json
{
  "client_secret": "pi_3abc123_secret_xyz789",
  "payment_intent_id": "pi_3abc123",
  "publishable_key": "pk_live_xxxxx",
  "amount": 3420,
  "currency": "usd"
}

Status: 201 Created

Response Fields

FieldTypeDescription
client_secretstringStripe client secret — pass this to stripe.confirmPayment() on the frontend
payment_intent_idstringStripe PaymentIntent ID — store this on your backend for order tracking
publishable_keystringStripe publishable key (same as from /config/stripe)
amountintegerConfirmed amount in cents
currencystringCurrency code

Error Responses

404 — No payment account configured:

json
{
  "error": "No payment account configured for this location",
  "organization_id": "org_00000k1L2m3N4o5"
}

The organization does not have a connected Stripe account. The account must be created and onboarded through the admin dashboard before payments can be accepted.

400 — Account cannot accept payments:

json
{
  "error": "Payment account cannot accept payments",
  "details": {
    "status": "restricted",
    "charges_enabled": false,
    "requirements": "incomplete"
  }
}

The connected Stripe account exists but has incomplete onboarding or is restricted.

422 — Validation error:

json
{
  "message": "The amount field must be at least 50.",
  "errors": {
    "amount": ["The amount field must be at least 50."]
  }
}

Get Payment Intent

Retrieve the current status of a payment intent. TransactCore syncs with Stripe in real-time for non-terminal statuses.

GET /v1/ecommerce/payment-intents/{payment_intent_id}

Authentication: OAuth Token

Scope: transactcore:payment-intents.read

Rate Limit: 100/min per IP

Path Parameters

ParameterTypeDescription
payment_intent_idstringThe Stripe PaymentIntent ID (e.g., pi_3abc123)

Response

json
{
  "data": {
    "payment_intent_id": "pi_3abc123",
    "amount": 3420,
    "currency": "usd",
    "status": "succeeded",
    "capture_method": "automatic"
  }
}

Response Fields

FieldTypeDescription
payment_intent_idstringStripe PaymentIntent ID
amountintegerAmount in cents
currencystringCurrency code
statusstringCurrent status (see table below)
capture_methodstringCapture method (automatic or manual)

Payment Intent Statuses

StatusDescription
requires_payment_methodAwaiting payment method from customer
requires_confirmationPayment method collected, awaiting confirmation
requires_actionCustomer authentication required (e.g., 3D Secure)
requires_capturePayment authorized, awaiting capture (manual capture only)
processingPayment is being processed
succeededPayment completed successfully
canceledPayment was canceled

Real-Time Sync

For non-terminal statuses (requires_*, processing), TransactCore syncs with Stripe before returning the response. This handles the race condition between client-side confirmation and webhook delivery — you can poll this endpoint immediately after stripe.confirmPayment() to get the latest status.

Error Response

404 — Payment intent not found:

json
{
  "error": "Payment intent not found"
}

Cancel Payment Intent

Cancel a payment intent. Use this when order creation fails after payment confirmation, to release the hold on the customer's card.

POST /v1/ecommerce/payment-intents/{payment_intent_id}/cancel

Authentication: OAuth Token

Scope: transactcore:payment-intents.create

Rate Limit: 100/min per IP

Path Parameters

ParameterTypeDescription
payment_intent_idstringThe Stripe PaymentIntent ID (e.g., pi_3abc123)

Response

json
{
  "status": "cancelled",
  "payment_intent_id": "pi_3abc123"
}

Error Responses

404 — Payment intent not found:

json
{
  "error": "Payment intent not found"
}

422 — Unable to cancel:

json
{
  "error": "Unable to cancel payment intent",
  "message": "This PaymentIntent has already succeeded and cannot be canceled."
}

A payment intent can only be canceled if it has not yet reached the succeeded status. If the payment has already been captured, you will need to create a refund instead.


Best Practices

  1. Always cancel on failure — If order creation fails after payment confirmation, cancel the payment intent to release the customer's funds.
  2. Store the payment_intent_id — Save the payment_intent_id on your backend when creating the intent, before sending the client_secret to the frontend. This ensures you can always look up or cancel the payment.
  3. Poll for status — After stripe.confirmPayment(), poll GET /payment-intents/{id} to confirm the final status before creating the order.
  4. Use metadata — Attach order identifiers to the payment intent metadata for easier reconciliation.
  5. Handle 3D Secure — Use redirect: 'if_required' in stripe.confirmPayment() to handle SCA/3DS challenges without forcing a redirect.

Changelog
DateChange
2026-03-19Added connected account lookup endpoint and fixed scope format.
2026-03-19Initial publication.

ShopHero CommerceCore Platform