FIRST-PARTY CUSTOMER-SIDE ADAPTER · V2

One redeemed permit. One exact Stripe payment.

A zero-dependency server adapter atomically redeems a signed, provider-bound MandateShield permit immediately before one automatic-capture Stripe create-and-confirm request.

DOWNLOAD

Keep Stripe credentials in your trusted gateway

curl --proto '=https' --tlsv1.2 -fsSLO   https://mandateshield.com/sdk/v1.10.0/mandateshield.mjs
curl --proto '=https' --tlsv1.2 -fsSLO   https://mandateshield.com/sdk/v1.10.0/mandateshield-gateway.mjs
curl --proto '=https' --tlsv1.2 -fsSLO   https://mandateshield.com/sdk/v1.10.0/mandateshield-stripe-payment-intents.mjs
curl --proto '=https' --tlsv1.2 -fsSLO   https://mandateshield.com/sdk/v1.10.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 -
fi

The 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 { createClient } from "./mandateshield.mjs"
import {
  createMandateShieldGateway
} from "./mandateshield-gateway.mjs"
import {
  createStripePaymentIntentsAdapter,
  STRIPE_PAYMENT_INTENTS_AUDIENCE
} from "./mandateshield-stripe-payment-intents.mjs"

const processor = createClient({
  apiKey: process.env.MANDATESHIELD_PROCESSOR_KEY
})
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: globalLinearizableGatewayJournal,
  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.
  • One create request with confirm=true, using the provider idempotency key carried by the freshly redeemed signed permit.
  • 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, currency, request ID and MandateShield metadata.

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.

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; the independent Stripe API lookup is.

This is a first-party integration module, not a Stripe partnership, certification, uptime promise or settlement guarantee.