Documentation Index
Fetch the complete documentation index at: https://86d.app/docs/llms.txt
Use this file to discover all available pages before exploring further.
86d separates payment logic into two layers: the @86d-app/payments module manages the intent lifecycle (create, confirm, cancel, refund), and a provider module (Stripe, PayPal, Square, or Braintree) handles the actual API communication. This split lets you swap payment providers without touching your checkout flow. All provider modules implement HMAC webhook verification using the Web Crypto API directly, so no extra crypto dependencies are required. Webhooks return 401 on invalid or expired signatures.
Stripe
PayPal
Square
Braintree
Install
npm install @86d-app/stripe @86d-app/payments
Add to config.json
{
"modules": [
"@86d-app/payments",
"@86d-app/stripe"
],
"moduleOptions": {
"@86d-app/stripe": {
"apiKey": "sk_live_...",
"webhookSecret": "whsec_..."
}
}
}
Then run:Set environment variables
Set these in your deployment environment (.env.local for local development):| Variable | Description |
|---|
STRIPE_SECRET_KEY | Your Stripe secret key. sk_live_... for production, sk_test_... for testing. |
STRIPE_WEBHOOK_SECRET | Signing secret from your Stripe webhook endpoint (whsec_...). |
In the Stripe dashboard, create a webhook endpoint pointing to:POST https://your-store.com/api/stripe/webhook
Select these events at minimum:
payment_intent.succeeded
payment_intent.payment_failed
payment_intent.canceled
charge.refunded
Copy the signing secret (whsec_...) and set it as STRIPE_WEBHOOK_SECRET.How the payment flow works
- Create intent. At checkout, the server calls
provider.createIntent({ amount, currency }) against the Stripe API. This returns a clientSecret in providerMetadata.
- Confirm on the client. Your checkout UI uses Stripe.js to call
confirmCardPayment(clientSecret) with the customer’s card details. No raw card data ever touches your server.
- Webhook confirms. Stripe sends a
payment_intent.succeeded event to /api/stripe/webhook. The module verifies the HMAC-SHA256 signature using a timing-safe comparison, then emits a payment.completed domain event that the payments module uses to finalize the order.
Webhook security
The Stripe module verifies each incoming webhook using the Stripe signing scheme:signed_payload = <timestamp> + "." + <raw_body>
expected_sig = HMAC-SHA256(webhook_secret, signed_payload)
Signatures are compared with constant-time comparison to prevent timing attacks. Webhooks with timestamps older than 5 minutes are rejected with 401 to protect against replay attacks. Without webhookSecret set, all requests are accepted, which is useful for local development with the Stripe CLI.Status mapping
| Stripe status | 86d status |
|---|
succeeded | succeeded |
canceled | cancelled |
processing, requires_capture | processing |
requires_payment_method, requires_confirmation, requires_action | pending |
Install
npm install @86d-app/paypal @86d-app/payments
Add to config.json
{
"modules": [
"@86d-app/payments",
"@86d-app/paypal"
],
"moduleOptions": {
"@86d-app/paypal": {
"clientId": "...",
"clientSecret": "..."
}
}
}
Then run:Set environment variables
| Variable | Description |
|---|
PAYPAL_CLIENT_ID | Your PayPal REST API client ID |
PAYPAL_CLIENT_SECRET | Your PayPal REST API client secret |
PAYPAL_WEBHOOK_ID | Webhook ID from your PayPal app configuration |
PAYPAL_SANDBOX | Set to "true" to use the PayPal sandbox environment |
In the PayPal developer dashboard, create a webhook pointing to:POST https://your-store.com/api/paypal/webhook
Subscribe to PAYMENT.CAPTURE.COMPLETED and PAYMENT.CAPTURE.DENIED at minimum.How the payment flow works
- The server creates a PayPal order via the PayPal Orders API and returns the order ID to the client.
- Your checkout UI renders the PayPal button SDK, which handles the buyer authentication flow.
- On approval, the client notifies the server, which captures the order.
- PayPal sends a webhook to confirm the capture; the module verifies the signature and emits
payment.completed.
Install
npm install @86d-app/square @86d-app/payments
Add to config.json
{
"modules": [
"@86d-app/payments",
"@86d-app/square"
],
"moduleOptions": {
"@86d-app/square": {
"accessToken": "...",
"webhookSignatureKey": "..."
}
}
}
Then run:Set environment variables
| Variable | Description |
|---|
SQUARE_ACCESS_TOKEN | Your Square access token (sandbox or production) |
NEXT_PUBLIC_SQUARE_APPLICATION_ID | Square application ID (client-side) |
NEXT_PUBLIC_SQUARE_LOCATION_ID | Square location ID (client-side) |
SQUARE_WEBHOOK_SIGNATURE_KEY | Signature key from your Square webhook subscription |
SQUARE_WEBHOOK_NOTIFICATION_URL | Public URL Square sends webhook events to |
In the Square Developer dashboard, open your application, go to Webhooks, and create a subscription pointing to:POST https://your-store.com/api/square/webhook
Subscribe to payment.created and payment.updated events. Copy the signature key and set it as SQUARE_WEBHOOK_SIGNATURE_KEY.How the payment flow works
- The server creates a Square payment using the Payments API with a nonce generated by the Square Web Payments SDK on the client.
- On success, Square sends a
payment.created webhook to confirm capture.
- The module verifies the HMAC-SHA256 signature and emits
payment.completed.
Install
npm install @86d-app/braintree @86d-app/payments
Add to config.json
{
"modules": [
"@86d-app/payments",
"@86d-app/braintree"
],
"moduleOptions": {
"@86d-app/braintree": {
"merchantId": "...",
"publicKey": "...",
"privateKey": "..."
}
}
}
Then run:Set environment variables
| Variable | Description |
|---|
BRAINTREE_MERCHANT_ID | Your Braintree merchant ID |
BRAINTREE_PUBLIC_KEY | Your Braintree public key |
BRAINTREE_PRIVATE_KEY | Your Braintree private key |
BRAINTREE_SANDBOX | Set to "true" to use the Braintree sandbox environment |
In the Braintree Control Panel, navigate to Settings → Webhooks and create a webhook pointing to:POST https://your-store.com/api/braintree/webhook
Subscribe to transaction_settled and transaction_settlement_declined notifications.How the payment flow works
- The server generates a client token using the Braintree SDK and returns it to the checkout UI.
- Braintree.js uses the token to render the Drop-in UI or Card Fields; the customer submits payment.
- The client sends the resulting payment nonce to the server, which submits the transaction to Braintree.
- Braintree sends a webhook to confirm settlement; the module verifies the signature and emits
payment.completed.
Webhook security across all providers
All four payment modules implement the same security model: webhook secrets are captured in a closure at module init time; each incoming request reads the raw body before any JSON parsing (required for HMAC integrity); signatures are verified with timing-safe comparison; and replay attacks are blocked by timestamp tolerance. No external crypto library is used. Verification runs on the Web Crypto API built into the runtime, keeping every payment module publishable as a standalone npm package.