diff --git a/CHANGELOG.md b/CHANGELOG.md index 776ade4ce..bd5fa2bc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## Next Release + +- Sends card details in the request body instead of the URL query string when `ReferralCustomer.addCreditCard` creates a Stripe token + ## v9.0.0-rc.1 (2026-09-08) - Breaking: Node 18+ is now required (built-in `fetch`) diff --git a/src/services/referral_customer_service.ts b/src/services/referral_customer_service.ts index c5ef0ce5c..4b24bf0cb 100644 --- a/src/services/referral_customer_service.ts +++ b/src/services/referral_customer_service.ts @@ -70,13 +70,15 @@ async function _sendCardDetailsToStripe( expirationYear: string, cvc: string, ): Promise { - const searchParams = new URLSearchParams({ + // Card details must travel in the form-encoded request body, never in the URL, + // so they cannot end up in access logs, proxy logs, or Referer headers. + const formBody = new URLSearchParams({ 'card[number]': number, 'card[exp_month]': expirationMonth, 'card[exp_year]': expirationYear, 'card[cvc]': cvc, }); - const url = `https://api.stripe.com/v1/tokens?${searchParams.toString()}`; + const url = 'https://api.stripe.com/v1/tokens'; try { const response = await fetch(url, { @@ -85,6 +87,7 @@ async function _sendCardDetailsToStripe( Authorization: `Bearer ${stripeKey}`, 'Content-Type': 'application/x-www-form-urlencoded', }, + body: formBody.toString(), }); if (!response.ok) {