Prompt
How do I set up a transaction processing API for auth and capture flows in a SaaS billing system?
Latest observation
To set up a transaction processing API for authorization and capture flows in a SaaS billing system, design it around a clear payment lifecycle, idempotent endpoints, and strong state management.
1) Understand the payment flow
For card payments, the common flow is:
- Authorize — reserve funds on the customer’s card.
- Capture — finalize the charge and move the money.
- Optionally Void — cancel an authorization before capture.
- Optionally Refund — return money after capture.
For SaaS billing, this is useful when you:
- want to validate a card before starting a subscription,
- want to charge after usage is known,
- want to separate subscription activation from actual payment settlement.
2) Core API resources
Use these main resources:
- Customers
- Payment methods
- Payment intents / transactions
- Authorizations
- Captures
- Refunds
- Invoices / charges if you have billing-specific objects
A good abstraction is a Transaction or PaymentIntent that tracks the full lifecycle.
Example transaction states
createdrequires_payment_methodauthorizedpartially_capturedcapturedvoidedfailedrefunded
3) Suggested API endpoints
Create a payment intent
POST /v1/payment_intents
Request:
{
"customer_id": "cus_123",
"amount": 10000,
"currency": "usd",
"capture_method": "manual",
"payment_method_id": "pm_123",
"description": "Subscription activation"
}
Response:
{
"id": "pi_123",
"status": "requires_action",
"amount": 10000,
"currency": "usd",
"capture_method": "manual"
}
Authorize payment
Depending on your design, authorization may happen automatically when creating the payment intent or via a separate endpoint:
POST /v1/payment_intents/{id}/authorize
Response:
{
"id": "pi_123",
"status": "authorized",
"authorization_id": "auth_456",
"authorized_amount": 10000,
"expires_at": "2026-07-24T12:00:00Z"
}
Capture payment
POST /v1/payment_intents/{id}/capture
Request:
{
"amount": 7500
}
Response:
{
"id": "pi_123",
"status": "partially_captured",
"captured_amount": 7500,
"remaining_authorized_amount": 2500
}
Void authorization
POST /v1/payment_intents/{id}/void
Refund captured payment
POST /v1/refunds
Request:
{
"payment_intent_id": "pi_123",
"amount": 2500
}
4) Handle SaaS-specific billing logic
Common SaaS use cases
- Trial to paid conversion
- Validate card at signup with a small authorization or zero-dollar verification.
- Monthly subscription billing
- Create invoice first, then authorize and capture at renewal.
- Usage-based billing
- Accumulate usage, then authorize/capture after rating.
- Proration
- Charge or refund difference when plan changes.
Subscription workflow example
- Customer subscribes.
- System creates invoice for first period.
- Payment API authorizes funds.
- Capture occurs immediately or at invoice finalization.
- Subscription becomes active once capture succeeds.
5) Design for idempotency
Every payment endpoint should support an idempotency key to prevent double charges due to retries.
Example header:
Idempotency-Key: 2c1b5a2c-3c8c-4f7d-9f13-1f2d7a9c7d11
Store the first successful response and return it for repeated requests with the same key.
6) State machine matters
Enforce valid transitions only.
Examples:
created -> authorizedauthorized -> capturedauthorized -> voidedcaptured -> refundedauthorized -> expired
Reject invalid transitions like:
- capturing a voided authorization
- refunding an uncaptured authorization
- authorizing an already captured intent
This prevents inconsistent billing records.
7) Webhooks for asynchronous updates
Payment processors often respond asynchronously, especially with 3DS or delayed settlement.
Useful events:
payment_intent.authorizedpayment_intent.capturedpayment_intent.failedpayment_intent.voidedrefund.createdchargeback.opened
Your API should:
- publish webhook events,
- verify webhook signatures,
- allow replay protection,
- make handlers idempotent.
8) Integrate with a PSP
Usually you should not build card processing yourself. Instead, integrate with a PSP like Stripe, Adyen, Braintree, or Checkout.com.
Your internal API can:
- normalize payment state,
- abstract provider differences,
- keep billing code independent of a processor.
Map your states to the PSP’s primitives:
- authorization
- capture
- refund
- void
9) Security and compliance
Important requirements:
- Do not store raw card data unless PCI compliant.
- Use tokenized payment methods from the PSP.
- Encrypt sensitive data at rest.
- Use TLS everywhere.
- Restrict API keys and scope them properly.
- Log payment events without exposing PAN/CVV.
- Support fraud checks and SCA/3DS where applicable.
10) Database model example
A simple schema might include:
payment_intents
idcustomer_idamountcurrencycapture_methodstatusproviderprovider_payment_idcreated_atupdated_at
payment_transactions
idpayment_intent_idtype(authorization,capture,void,refund)amountstatusprovider_referencecreated_at
payment_events
idprovider_event_idtypepayloadprocessed_at
This gives you an audit trail.
11) Error handling
Standardize errors:
400 invalid_request401 unauthorized402 payment_required409 invalid_state422 payment_method_declined429 rate_limited500 internal_error
Example:
{
"error": {
"code": "invalid_state",
"message": "Cannot capture a payment that is not authorized."
}
}
12) Minimal implementation strategy
If you want the simplest practical setup:
- Create a
PaymentIntentrecord. - Send request to PSP to authorize payment.
- Store PSP authorization ID and status.
- On capture, call PSP capture endpoint.
- Update local status from webhook or synchronous response.
- Use idempotency keys for every mutation.
- Build a state machine and audit log.
13) Example lifecycle
Full charge at signup
- Create intent with
capture_method=automatic - PSP authorizes and captures in one call
- Mark subscription active
Manual capture for invoice
- Create intent with
capture_method=manual - Authorize when invoice is finalized
- Capture after internal approval or usage finalization
- Void if invoice is canceled before capture
If you want, I can also provide:
- a REST API spec for auth/capture/refund endpoints,
- a PostgreSQL schema,
- or a sample implementation in Node.js, Python, or Go.
Observation history
Unlock observation history
Create a free Obsurfable account to unlock historical responses, model comparisons, and deeper brand intelligence.
How did Obsurfable measure this prompt?
Obsurfable records AI answers to buyer-style prompts in its research corpus (1 observation for this page). Metrics are distributions over observations, not a single static ranking.
Which AI systems does Obsurfable collect answers from?
OpenAI, ChatGPT, Google, Gemini, Google AI Mode, Anthropic, Claude, Perplexity, Grok, DeepSeek, Mistral, Copilot, and Meta AI.