Architecture
AccruPay is a payment router. It sits between your application and payment providers — your backend starts and verifies sessions through AccruPay, while your frontend submits card data directly to the provider through the React SDK. Your server never handles raw card numbers.
Request flow
Your Backend AccruPay Provider
│ │ │
│──(1) start session──────▶│ │
│◀─────── session.id ──────│ │
│ │ │
│──(2) session.id ──▶ Your Frontend │
│ │ │
│ Your Frontend │
│ │────(3) card data + session ──▶│
│ │◀────── provider result ───────│
│ │ │
│──(4) verify(session.id)─▶│ │
│◀────── transaction ──────│ │
│ │ │
Step-by-step
| Step | Actor | Action |
|---|---|---|
| 1 | Your backend | Calls clientSessions.payments.start() with transaction details. AccruPay creates a session and returns session.id. |
| 2 | Your backend | Passes session.id to your frontend (e.g., as part of a checkout API response). |
| 3 | React SDK | Customer fills card fields and submits. The SDK sends card data directly to the provider. Your server is never in this path. |
| 4 | Your backend | Calls clientSessions.payments.verify() with session.id. AccruPay returns the authoritative transaction result. |
Server / client boundary
| Responsibility | Runs on |
|---|---|
| Start session | Backend — requires apiSecret |
Store apiSecret | Backend only — never expose to frontend |
| Render payment fields | Frontend — React SDK |
| Submit card data | Frontend → Provider — React SDK sends directly |
| Verify result | Backend — verify() is the source of truth |
Your frontend only needs two public values: merchantPublicId (static, safe to embed) and session.id (per-transaction, scoped, expires).
PCI scope
Card data follows this path:
Browser (React SDK fields) ──▶ Provider SDK ──▶ Provider
Your server is never in the card data path. AccruPay itself does not receive or store raw card numbers. This architecture keeps your integration out of PCI DSS scope for card data handling — your server only exchanges session identifiers and transaction metadata.
AccruPay's hosted field components render inside provider-controlled iframes or secure input contexts, depending on the provider. Raw card values are never accessible to your JavaScript.
Verify is the source of truth
verify() is the only signal you should trust for transaction outcome. Do not use the frontend callback as a confirmation trigger.
Why the frontend callback is unreliable:
- The customer's browser may close, navigate away, or lose connectivity after the provider processes the payment but before your callback fires.
- Network errors between the provider and your callback handler can silently drop success events.
- Malicious clients can forge or replay callback payloads.
What happens if you trust the frontend callback instead:
- Missed payments: Provider charges succeed; your system records nothing. Customer is charged but no order is created.
- Double charges: You retry a
start()on each callback failure, creating multiple sessions against the same intended purchase. - Fulfillment without payment: A forged callback triggers order fulfillment before payment is confirmed.
Correct pattern:
frontend callback fires (informational only)
│
▼
backend calls verify()
│
▼
act on transaction.status from verify()
Call verify() from your backend after the frontend signals completion. Build your fulfillment, confirmation email, and inventory updates on the verify() result — not on anything that arrived through the browser.
Next steps
- Payment Providers — how provider routing works
- Environments — sandbox vs production
- Quick Start — complete working example