Integrate Payments

Payment integration is the task every team knows is coming and no one enjoys building. Provider SDKs differ, webhook signatures are easy to get wrong, and a single missed edge case means money either fails to capture or gets double-charged. Sun Agent Kit scaffolds complete, production-ready payment integrations — including provider-specific webhook handlers, idempotency keys, and reconciliation logging — so your team ships a working checkout, not a prototype.

Overview

Goal: Integrate a payment provider end-to-end: checkout session, webhook handling, subscription management, and refunds
Time: 25–50 minutes (vs 5–10 hours of reading docs, writing boilerplate, and debugging webhooks)
Agents used: planner, implementer, tester, reviewer
Commands: /sk:cook, /sk:scout, /sk:security-scan, /sk:test

Prerequisites

  • Sun Agent Kit installed and authenticated (installation guide)
  • A running backend application (Node.js/Express, Python/FastAPI, Laravel, or Spring Boot)
  • Payment provider account with API keys available (stored in .env, never hardcoded)
  • Webhook endpoint accessible from the internet (use ngrok or similar for local dev)
  • Database with a payments or orders table (agent will create migration if missing)

Step-by-Step Workflow

Before generating anything, the agent maps what is already in place — existing cart logic, order models, and any partial integrations.

/sk:scout "payment, checkout, order, cart, billing, subscription, invoice"

What happens: The agent:

  1. Scans the repository for payment-related patterns in controllers, models, and configuration
  2. Maps the integration surface — framework, ORM, auth method
  3. Identifies what exists (order models, cart logic) and what is missing (webhook handler, subscription management)
  4. Generates an integration blueprint saved to plans/reports/

Step 2: Select your payment provider and generate the integration

Tell the agent which provider (or providers) you need and what payment flows are required.

/sk:cook "Stripe payment integration with one-time checkout and subscription support, using Prisma and Express"

What happens: The agent:

  1. Installs the provider SDK and creates checkout session handlers with idempotency key patterns
  2. Generates subscription management routes (subscribe, cancel, check status)
  3. Builds a webhook handler with signature verification for key event types
  4. Creates database migrations for subscriptions and payment audit logs
  5. Writes unit tests covering happy path and failure scenarios

Step 3: Add a second payment provider (SePay for Vietnam)

For projects serving both international and Vietnamese markets, add SePay as an alternative payment method alongside Stripe.

/sk:cook "Add SePay payment gateway alongside existing Stripe integration. SePay handles VND bank transfers for Vietnamese customers."

What happens: The agent:

  1. Generates SePay checkout handler with VND currency handling and QR code generation
  2. Builds a SePay webhook handler with HMAC-SHA256 signature verification
  3. Implements currency-based routing logic (VND → SePay, others → Stripe)
  4. Writes tests for the SePay payment flows

Step 4: Configure webhooks and test locally

Webhooks are the part that breaks silently in production. The agent sets up verification and test scripts.

/sk:cook "Test payment webhooks locally with stripe-cli and verify all event types are handled"

What happens: The agent:

  1. Verifies local webhook configuration and secrets in .env
  2. Generates a webhook test script covering key event types
  3. Validates that each event handler creates the expected database records

Step 5: Security scan the payment code

Payment code is a high-value target. Run a focused scan before committing.

/sk:security-scan src/payments/ src/webhooks/

What happens: The agent:

  1. Checks that webhook signatures are verified before processing
  2. Confirms idempotency keys are used on all mutation endpoints
  3. Verifies no raw card data is stored or logged
  4. Confirms amount validation is server-side (no client-side price trust)
  5. Flags any missing security measures (e.g., rate limiting)

Complete Example: Vietnamese E-Commerce Needs SePay + Stripe Dual Integration

Scenario

A Vietnamese fashion e-commerce client is expanding internationally. Their existing Laravel backend has a basic cash-on-delivery flow. The new requirement: Vietnamese customers pay via SePay bank transfer (QR code), international customers pay via Stripe card. Both flows must update the same orders table, and the business owner needs a single reconciliation report. The deadline is end of sprint — five working days.

Commands chained

# Day 1 morning — understand what exists
/sk:scout "payment, order, checkout, cart"

# Day 1 afternoon — scaffold both providers at once
/sk:cook "Dual payment integration for Laravel: Stripe for international customers (USD/EUR), SePay for Vietnamese customers (VND). Shared orders table, unified reconciliation log."

# Day 2 morning — add subscription support (client also sells monthly boxes)
/sk:cook "Add monthly subscription support to Stripe integration — Stripe Billing, plan management, cancellation flow"

# Day 2 afternoon — test webhook handling thoroughly
/sk:test "comprehensive webhook tests for both Stripe and SePay handlers — test idempotency, signature failure, duplicate event rejection"

# Day 3 — security review before demo
/sk:security-scan --full

# Day 4 — generate client-facing integration documentation
/sk:docs "Payment integration guide for client handoff — cover both Stripe and SePay, webhook setup, .env variables, reconciliation query"

Result

By day four, the client has a working dual-provider checkout, a tested webhook pipeline that handles all edge cases, and a reconciliation query that joins both payment sources into a single dashboard view. The handoff document gives their in-house team everything they need to manage the integration going forward.

Time Comparison

TaskManualWith Sun Agent Kit
Reading Stripe SDK docs + boilerplate90 minminutes
Writing checkout session + error handling60 minminutes
Building webhook handler with sig verification45 minminutes
SePay API research + implementation90 minminutes
Writing tests for payment flows60 minminutes
Security review of payment code30 minminutes
Total~6 hoursunder 30 minutes

Best Practices

1. Always validate amounts server-side, never trust the client ✅

The agent enforces this pattern by default — checkout sessions always derive the price from your database using a price_id or product_id, never from a client-supplied amount field. This prevents price manipulation attacks.

2. Use the payment_events audit log for all webhook processing ✅

The scaffolded webhook handler writes every received event to a payment_events table before processing it. This gives you idempotency (skip already-processed event IDs), a reconciliation trail, and a debugging surface when payments “mysteriously” do not settle.

3. Never log the full payment payload in production ❌

Stripe and SePay payloads can contain partial card numbers and sensitive billing details. The generated code logs event type and event ID only. Do not add console.log(event) or Log::debug($event) to debug a webhook issue in production — use the provider’s dashboard instead.

4. Do not skip the webhook signature verification step, even in staging ❌

It is tempting to comment out signature verification when debugging locally. This creates a habit that eventually makes it into a production deploy. Always keep signature verification active and use the provider’s CLI tools (e.g., stripe listen) for local testing.

Troubleshooting

Problem: Webhook events are received but orders are not created — no error in logs

Solution: The most common cause is a missing Content-Type: application/json on the raw request body used for signature verification. Express (and many frameworks) parse the body before the webhook handler sees it, destroying the raw buffer needed for HMAC verification. The generated handler uses express.raw({ type: 'application/json' }) as middleware — ensure no global JSON parser runs before it on the webhook route.

Problem: SePay QR code payment never triggers the webhook

Solution: SePay requires the webhook callback URL to be registered in the merchant dashboard and must be HTTPS. In local development, confirm your ngrok tunnel URL is registered. In staging, confirm the server is reachable from SePay’s IP range and that the SEPAY_WEBHOOK_SECRET in .env matches the value in the dashboard.

Problem: Stripe subscription cancellation webhook fires but user access is not revoked

Solution: Check that your handler listens for customer.subscription.deleted (not customer.subscription.updated). Cancellation at period end fires customer.subscription.updated with cancel_at_period_end: true — access should remain active until current_period_end. The generated handler covers both events; verify you have not removed the customer.subscription.updated branch.

Problem: Duplicate orders created on retry

Solution: Run /sk:cook "Add idempotency key handling to Stripe checkout session creation" to scaffold the idempotency pattern. The Stripe API accepts an Idempotency-Key header — generate it as sha256(userId + cartId + timestamp_minute) so retries within the same minute reuse the existing session.

Next Steps


Key takeaway: Payment integration is too error-prone to hand-code under deadline pressure — the agent scaffolds the complete, security-hardened integration so your team focuses on business logic, not boilerplate.