DOWNLOAD
Keep Stripe credentials in your trusted gateway
# Preferred: install the pinned package and PostgreSQL driver
npm install https://mandateshield.com/packages/npm/1.13.0/mandateshield-sdk-1.13.0.tgz pg
# Verify the immutable package checksum before production pinning
curl --proto '=https' --tlsv1.2 -fsS https://mandateshield.com/packages/npm/1.13.0/SHA256SUMS
# Alternative: checksum-pinned standalone ESM modules
curl --proto '=https' --tlsv1.2 -fsSLO https://mandateshield.com/sdk/v1.13.0/mandateshield.mjs
curl --proto '=https' --tlsv1.2 -fsSLO https://mandateshield.com/sdk/v1.13.0/mandateshield-gateway.mjs
curl --proto '=https' --tlsv1.2 -fsSLO https://mandateshield.com/sdk/v1.13.0/mandateshield-stripe-payment-intents.mjs
curl --proto '=https' --tlsv1.2 -fsSLO https://mandateshield.com/sdk/v1.13.0/SHA256SUMS
if command -v sha256sum >/dev/null 2>&1; then
grep -E ' (mandateshield|mandateshield-gateway|mandateshield-stripe-payment-intents)\.mjs$' SHA256SUMS | sha256sum -c -
else
grep -E ' (mandateshield|mandateshield-gateway|mandateshield-stripe-payment-intents)\.mjs$' SHA256SUMS | shasum -a 256 -c -
fiThe customer-side adapter keeps the charge-creation Stripe secret, PaymentMethod ID and client secret inside your gateway. If you enable hosted reconciliation, configure a separate restricted Stripe lookup key; MandateShield stores it encrypted and uses it only to retrieve and verify the bound PaymentIntent outcome.
INTEGRATE
Bind the authorized envelope to Stripe
import { Pool } from "pg"
import { MandateShield } from "@mandateshield/sdk"
import {
createMandateShieldGateway
} from "@mandateshield/sdk/gateway"
import {
createPostgresGatewayJournal
} from "@mandateshield/sdk/gateway/postgres"
import {
createStripePaymentIntentsAdapter,
STRIPE_PAYMENT_INTENTS_AUDIENCE
} from "@mandateshield/sdk/gateway/stripe-payment-intents"
const processor = new MandateShield({
apiKey: process.env.MANDATESHIELD_PROCESSOR_KEY
})
const database = new Pool({
connectionString: process.env.GATEWAY_DATABASE_URL,
ssl: { rejectUnauthorized: true }
})
const journal = createPostgresGatewayJournal({
database,
namespace: "stripe-payments.production"
})
await journal.checkReadiness()
const stripe = createStripePaymentIntentsAdapter({
secretKey: process.env.STRIPE_SECRET_KEY,
stripeAccountId: process.env.STRIPE_CONNECTED_ACCOUNT,
merchantId: "merchant_storefront",
processorAudience: STRIPE_PAYMENT_INTENTS_AUDIENCE,
apiVersion: process.env.STRIPE_API_VERSION
})
const gateway = createMandateShieldGateway({
journal,
mandateShield: processor,
provider: stripe
})
const finalPurchase = {
...finalPurchaseFacts,
payee_identity: {
profile: "MANDATESHIELD_PAYEE_IDENTITY_V1",
provider: "STRIPE",
merchant_id: finalPurchaseFacts.merchant_id,
binding: {
connected_account_id: process.env.STRIPE_CONNECTED_ACCOUNT,
merchant_account_id: process.env.STRIPE_CONNECTED_ACCOUNT
},
verification: {
method: "STRIPE_ACCOUNT_CONFIGURATION",
verifier: "customer-stripe-gateway",
evidence_ref:
`urn:stripe:connected-account:${process.env.STRIPE_CONNECTED_ACCOUNT}`
}
}
}
// Sign and strictly verify this exact finalPurchase before gateway execution.
const result = await gateway.execute({
receipt_id: reservation.signed_receipt.receipt_id,
compact: reservation.signed_receipt.compact,
expected_envelope: finalPurchase,
expected_audience: STRIPE_PAYMENT_INTENTS_AUDIENCE,
provider_input: {
payment_method_id: trustedServerSidePaymentMethodId
}
})Every process and region for this Gate must use the same globally shared, linearizable journal namespace. Its required GLOBAL_SHARED_LINEARIZABLE declaration is a customer assertion, not an independent database attestation; per-region or split-brain journals are unsafe.
The adapter declares SIGNED_EXECUTION_PERMIT_V1. The Gate includes the exact Stripe account, environment, merchant, amount, currency, request resource and SHA-256 form-body digest in the CONSUME binding. It then atomically redeems the resulting permit before Stripe can be called. Signature-only or replayed permits never unlock submission.
SUPPORTED V1 PROFILE
Deliberately narrow payment semantics
- Card PaymentIntents with automatic capture only.
- One explicitly configured Stripe connected account.
- Canonical payee identity binds both connected and merchant account into the signed final envelope; merchant_id alone is rejected.
- Exact authorized minor units, currency and merchant mapping.
- Immutable envelope and PaymentMethod snapshots before any network wait; journal identity also binds the signed-receipt digest.
- An idempotent create-and-confirm operation with
confirm=true, using the provider idempotency key carried by the freshly redeemed signed permit. MandateShield does not claim exactly-once network delivery to Stripe. - The permit body digest covers the exact amount, currency, automatic-capture mode, card method, PaymentMethod ID and MandateShield metadata sent to Stripe.
- The pre-issued provider request ID is included as
metadata[mandateshield_provider_request_id]and must be echoed exactly during response checks and reconciliation, enabling provider-side lookup without changing the signed body after permit issuance. - Customer-supplied durable compare-and-set journal required.
- Stripe credentials can target only the official API; custom endpoints are restricted to an explicit localhost test mode.
FAILURE SEMANTICS
Unknown never means safe to retry
Succeeded commits. A failed or action-required card attempt is first canceled with a stable key and releases only after Stripe returns the exact bound PaymentIntent with status=canceled. Failed cancellation, processing, network ambiguity, malformed responses and binding mismatches remain pending or unknown.
This server-only profile never exposes client_secret or next_action; an intent requiring customer action follows the same verified-cancel policy. An unknown create without a provider reference can reuse its exact permit-bound provider key only inside a fixed 20-hour recovery window. After that window, the adapter makes no new create request and requires provider-side reconciliation.
After Stripe returns a PaymentIntent reference, report the exact provider_submission_id, permit_id, claim_id, payment reference and bounded caller outcome to POST /api/v2/provider-submissions. The caller observation is stored as CALLER_ASSERTED evidence, then MandateShield retrieves that exact PaymentIntent through the configured restricted key and re-checks account, environment, amount, received amount, currency, PaymentMethod, capture and confirmation modes, card method, request ID, payee metadata and the permit-bound request-body digest.
Only an exactly bound succeeded or canceled PaymentIntent can produce PROVIDER_API_VERIFIED evidence with independent_verification=true and autonomously COMMIT or RELEASE. Processing, API unavailability, not-found responses and binding conflicts remain pending or conflicting and stay fail-closed for reconciliation.
In this API field, independent_verification means that MandateShield retrieved the configured Stripe object without trusting the caller's asserted result and matched it to the stored execution permit. It does not mean verification by an independent organization, Stripe attestation, certification or audit.
A correctly signed Stripe webhook may wake reconciliation and preserve an authenticated hint, but remains CALLER_ASSERTED. It is not the trust source for a terminal transition; MandateShield must retrieve and recheck the configured Stripe PaymentIntent without trusting that hint.
This is a first-party integration module, not a Stripe partnership, certification, uptime promise or settlement guarantee.