条纹支付:收到错误消息:客户cus_*****没有与ID tok_*****关联的卡片。

54

在测试模式下,当我创建一个新客户并尝试付款时,出现了以下错误。

客户 cus_7Zz2BCnybIZLGw 没有与 ID 为 tok_17Kp8GAwLkQPB7OqrrM73VVI 的卡片相关联。

我使用的卡号是: 4242424242424242 过期月份: 12 过期年份: 2016

返回的响应是:

Array
(
    [charge_status] => 
    [error_info] => Array
        (
            [type] => invalid_request_error
            [message] => Customer cus_7Zz2BCnybIZLGw does not have a linked card with ID tok_17Kp8GAwLkQPB7OqrrM73VVI.
            [param] => card
            [code] => missing
        )

    [message] => Customer cus_7Zz2BCnybIZLGw does not have a linked card with ID tok_17Kp8GAwLkQPB7OqrrM73VVI.
)

输入充电数据为:

 $customer = Stripe_Customer::create(array(
      'account_balance' => 100,
      'source' => $token,
      'email' => strip_tags(trim($email))
    )
  );

$customer_id = $customer->id;

$charge   = array(
                'card'          => 4242424242424242, 
                'amount'        => 100, 
                'currency'      => 'cad', 
                'receipt_email' => test@test.com,
                'description'   => 'my payment',
                'customer'      => $customer_id
              );
3个回答

111

有三种不同的方式创建费用

  • 仅使用source参数。在这种情况下,source需要是tokensource ID(由CheckoutStripe.js创建),即以tok_src_开头的字符串。

  • 仅使用customer参数。在这种情况下,customer需要是customer ID,即以cus_开头的字符串。将会收取客户的默认支付来源。

  • 同时使用customersource参数。在这种情况下,customer需要是先前提到的客户ID,但source应该是已经附加到客户的付款来源的ID。付款来源可以是cards(ID以card_开头)、bank accounts(ID以ba_开头)或sources(ID以src_开头)。

在您的情况下,您在source参数中传递了一个令牌ID和在customer参数中传递了一个客户ID。
如果这是一张新卡,您应该先使用令牌在客户上创建一张卡,然后使用卡ID创建付款。如果该卡已为此客户保存,则无需再次收集卡信息(因此根本不需要创建令牌)。

@Ywain,如果使用Apple Pay令牌呢?似乎不可能将Apple Pay交易与客户帐户关联,并让他们每次使用Price Sheet和TouchID UI。要么我将令牌转换为卡片,然后它就像存储的卡片一样行事,而不是像Apple Pay一样,要么每次都获取新的令牌,并且没有与客户帐户相关联的收费历史记录。 - emkman
2
@emkman:我想你可以使用Apple Pay每次创建一个新的令牌,将令牌附加到现有的客户对象上,使用客户对象创建收费,然后从Apple Pay令牌中删除卡。这样,您就可以使用Apple Pay对每个交易进行身份验证,同时仍然在客户对象上保留收费历史记录。 - Ywain
@Ywain。我刚刚阅读了这篇文章,虽然它很有帮助,但不幸的是它并没有根据原始问题解释错误发生的原因(也许我错过了一些显而易见的东西)。 - JackDev
2
@IonicBurger OP在“customer”参数中提供了客户ID,在“source”参数中提供了令牌ID。当同时提供“customer”和“source”参数时,“source”必须是卡片ID。如果“source”是一个令牌ID,则不应传递“customer”参数。 - Ywain

7

这段代码对我有用过。它也可能对你有用。

Stripe\Stripe::setApiKey(env('STRIPE_SECRET'));

$customer = \Stripe\Customer::create([
    'name' => 'Jenny Rosen',
    'email' => 'jenyy@hotmail.co.us',
    'address' => [
        'line1' => '510 Townsend St',
        'postal_code' => '98140',
        'city' => 'San Francisco',
        'state' => 'CA',
        'country' => 'US',
    ],
]);

\Stripe\Customer::createSource(
    $customer->id,
    ['source' => $request->stripeToken]
);

Stripe\Charge::create ([
    "customer" => $customer->id,
    "amount" => 100 * 100,
    "currency" => "usd",
    "description" => "Test payment from stripe.test." , 
]);

我错过的部分是\Stripe\Customer::createSource。现在它可以工作了。谢谢! - Nitin Shinde

0

这是我的解决方案流程,对我来说可以防止重复付款并完成流程,如有必要,请随意改进

在条纹表单中添加:
<input type="hidden" name="idempotency" id="idempotency" value="{{ genRandAlphaNumber(8)}}">

genRandAlphaNumber是一个函数,用于创建一个字符串以避免在stripe中进行重复支付。
function genRandAlphaNumber($length) {
     $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
     $randomString = '';
     for ($i = 0; $i < $length; $i++) {
         $randomString .= $characters[rand(0, strlen($characters) - 1)];
     }
     return $randomString;
}
  • 在Stripe后期处理中

    $user = auth()->user();
    
    
     try {
         $cart = new Cart();
         $cart->user_id = $user->id;
         $cart->save();
    
         $description = '描述';
    
         Stripe\Stripe::setApiKey(env('STRIPE_SECRET'));
    
         $idempotency = preg_replace('/[^a-z\d]/im', '', $request->idempotency);
         $stripe_token = $request->stripeToken;
    
         //如果用户的stripe_token为空,则创建用户      
         if (!$user->stripe_token) {
             $result = \Stripe\Customer::create(array(
                 'name'   => $user->name .' '. $user->name,
                 "email"  => $user->email,
                 "source" => $stripe_token
             ));
    
             if($result && $result->id) {
                 $user->stripe_id = $result->id;
                 $user->stripe_token = $stripe_token;
                 $user->save();
             }
         }
    
    
         //如果用户有token
         if($user->stripe_token) {
             // 使用您的金额向客户收费
             $result = \Stripe\Charge::create(array(
                 "currency"  => 'usd',
                 "customer"  => $user->stripe_id,
                 "amount"    => $cart->price  * 100,
                 "description" => $description
                 ),
                 ["idempotency_key" => $idempotency,]
             );
    
             Session::flash('success', '付款成功!');
    
             $cart->status = 4;
             $cart->save();
    
    
             return redirect()->route('cart.finish', $cart->id);
         }
    
    
    
     } catch (\Exception $ex) {
         if ($ex->getStripeCode() == "idempotency_key_in_use") {
             sleep(2);
             //搜索最后一个车
             $cart = Cart::whereUser_id($user->id)->whereStatus(4)->orderBy('id', 'DESC')->first();
             if (!is_null($cart)) {
                 Session::flash('success', '支付成功!,防止重复付款');
                 return redirect()->route('cart.finish', $cart->id);
             }
             return back();
         }
    
         if ($ex->getJsonBody()['error']['type'] == "idempotency_error") {
             $cart = Cart::whereUser_id($user->id)->whereStatus(4)->orderBy('id', 'DESC')->first();
             if (!is_null($cart)) {
                 Session::flash('success', '支付成功!');
                 return redirect()->route('cart.membership.update', $cart->id);
             }
             return back();
         }
    
         return $ex->getMessage();
     }

  • 网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接