You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用Stripe获取银行账户Token遇400错误,求正确实现方案

Fixing the 400 Error When Fetching a Stripe Bank Account Token

Hey there, let's walk through fixing that 400 error and cover the best practices for getting a Stripe bank account token. First, let's break down what's wrong with your current code, then show you the correct implementation—including a critical security note you might have missed.

What's Causing the 400 Error?

Your code has two main issues triggering the bad request:

  1. Incorrect Request Body Format: Stripe's API expects application/x-www-form-urlencoded data, but you're passing a raw JSON object. Even with the right Content-Type header, Angular's HttpClient won't auto-convert the object to form-encoded data for you.
  2. Incorrect Parameter Structure: Stripe requires bank account details to be nested under a bank_account key (e.g., bank_account[country]), not top-level keys like country.
  3. Bonus: Misplaced Success Check: Stripe's token response doesn't include a status: "successful" field—instead, a successful response will have an id (the token itself).

Fixed Implementation (Direct API Call)

If you need to stick with a direct API call (though we'll cover a better approach below), here's how to adjust your code:

First, add a helper function to encode your object into form-encoded data:

private encodeFormData(data: Record<string, string>): string {
  return Object.keys(data)
    .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`)
    .join('&');
}

Then update your token fetching function:

getTokenOFBankAccount(myObject: any): Observable<any> {
  this.spinner.show();

  // Format data correctly with nested bank_account keys
  const formData = this.encodeFormData({
    'bank_account[country]': myObject.country,
    'bank_account[currency]': myObject.currency,
    'bank_account[routing_number]': myObject.routing_number,
    'bank_account[account_number]': myObject.account_number,
    'bank_account[account_holder_name]': myObject.account_holder_name,
    'bank_account[account_holder_type]': myObject.account_holder_type,
  });

  const headers = new HttpHeaders({
    Authorization: `Bearer ${environment.stripeToken}`,
    'Content-Type': 'application/x-www-form-urlencoded'
  });

  return this.http.post<any>("https://api.stripe.com/v1/tokens", formData, { headers })
    .pipe(
      tap(response => {
        this.spinner.hide();
        // Check for the token ID instead of a "successful" status
        if (response.id) {
          console.log('Bank account token generated:', response.id);
          // Handle successful token retrieval here
        }
      }),
      catchError(error => {
        this.spinner.hide();
        // Stripe returns detailed error info in error.error
        console.error('Token generation failed:', error.error.message);
        return throwError(() => new Error(error.error.message));
      })
    );
}

Critical Security Note: Never Expose Your Stripe Secret Key in Frontend Code!

Wait a second—if environment.stripeToken is your Stripe Secret Key, you're making a huge security mistake. Secret Keys should never be used in client-side code (they can be easily extracted by anyone inspecting your app).

Instead, use Stripe's client-side libraries (Stripe.js/Elements) which only require your Publishable Key, and handle sensitive data securely without exposing your Secret Key.

Stripe Elements is the safest and easiest way to collect bank account details and generate tokens. It handles PCI compliance, form validation, and avoids exposing sensitive keys. Here's how to implement it:

  1. Install the necessary packages:
npm install @stripe/stripe-js @stripe/react-stripe-js
  1. Initialize Stripe with your Publishable Key:
// src/app/stripe.config.ts
import { loadStripe } from '@stripe/stripe-js';
export const stripePromise = loadStripe(environment.stripePublishableKey);
  1. Create a bank account form component using Elements:
import { Elements, BankAccountElement, useStripe, useElements } from '@stripe/react-stripe-js';
import { useState } from 'react';

function BankAccountForm() {
  const stripe = useStripe();
  const elements = useElements();
  const [loading, setLoading] = useState(false);

  const handleSubmit = async (event: React.FormEvent) => {
    event.preventDefault();
    if (!stripe || !elements) return;

    setLoading(true);

    // Create token using Stripe Elements
    const result = await stripe.createToken(elements.getElement(BankAccountElement), {
      account_holder_name: 'Jenny Rosen',
      account_holder_type: 'individual',
    });

    setLoading(false);

    if (result.error) {
      console.error('Error:', result.error.message);
      // Show error message to user
    } else {
      console.log('Token:', result.token.id);
      // Send the token to your backend for further processing
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <BankAccountElement 
        options={{ 
          country: 'US', 
          currency: 'usd',
          style: { base: { fontSize: '16px' } }
        }} 
      />
      <button type="submit" disabled={!stripe || loading}>
        {loading ? 'Loading...' : 'Generate Token'}
      </button>
    </form>
  );
}

// Wrap your form in Elements in your parent component
export function App() {
  return (
    <Elements stripe={stripePromise}>
      <BankAccountForm />
    </Elements>
  );
}

Why This Is Better:

  • PCI Compliance: Stripe Elements handles all sensitive data collection, so you don't have to worry about storing or transmitting sensitive bank details.
  • Security: No Secret Key exposed in frontend code—only your Publishable Key is used client-side.
  • Ease of Use: Built-in validation, error handling, and customizable UI components.
  • Less Code: No need to manually encode form data or handle low-level API calls.

Final Tips

  • Always check Stripe's error response body (in error.error) for specific details about why the 400 error occurred—this will help you debug issues quickly.
  • For testing, use Stripe's official test bank account details: routing number 110000000 and account number 000123456789 (which you're already using, good job!).

内容的提问来源于stack exchange,提问作者Ahmad Awais

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:21:32