奇怪的Laravel/Stripe/Cartalyst错误,使用相同的Stripe代币随机充电两次



我创建了一个Android移动应用程序,它集成了Stripe来接受卡支付。在我的Android应用程序中,我向我的Laravel API发送一组参数,其中一个是Stripe令牌。

10次中有9次,我的服务器按预期处理此问题。它将使用Stripe Cartalyst创建一个收费对象,并创建一个费用,假设没有抛出异常(如坏卡详细信息或服务器错误(,然后我的api将继续处理成功的付款,并在DB中进行一些插入,之后它将向客户端返回201 HTTP状态代码,然后我在Android端处理此问题。

不过,我遇到的错误(或我做得不对的事情(是随机发生的。有时,我的API会从Stripe抛出一个未捕获的异常,解释令牌(加密的Stripe卡详细信息(,只能使用一次。我在Android端做了一些调试,可以验证我只在发送令牌的地方发出1个HTTP请求,并在我的Laravel API上进行了一些登录,发现了一些奇怪的事情。

我的发现在API上,我将流程的一些关键时刻打印到日志文件中。

  1. 打印令牌变量
  2. 当进入try块以创建充电对象时
  3. 如果在Android应用程序上选择了"交付"(可能是一个毫无意义的日志(
  4. 正在检查一些项目(这是一个从Android应用程序传递的数组
  5. 在充电结束时,在将201返回给客户端之前,打印某种成功消息

然后我继续重新创建这个错误,在大约20/30个订单之后,它发生了,这就是发生的过程:

首先,在Android应用程序上,发送了以下令牌(并打印到控制台(;

tok_1DiK0uKIdjSiVG8mn8CV2iim

经过检查,这个HTTP请求只触发过一次,所以我认为这不是安卓系统的问题。

接下来,在API日志文件上,出现以下情况:

2018-12-17 11:11:56]本地。调试:令牌:tok_1DiK0uKIdjSiVG8mn8CV2iim〔2018-12-17 11:11:56〕当地。调试:创建条带收费

〔2018-12-17 11:11:59〕当地。调试:令牌:tok_1DiK0uKIdjSiVG8mn8CV2iim

〔2018-12-17 11:11:59〕当地。调试:创建条带收费

〔2018-12-17 11:12:00〕当地。错误:当前有另一个正在使用此Stripe令牌进行请求(这可能意味着您点击两次,另一笔费用仍在继续(:tok_1DiK0uKIdjSiVG8mn8CV2iim。如果充电成功{"exception":"[object](Cartalyst\Stepe\Exception\MissingParameterException(代码:400(:当前有另一个正在使用此Stripe令牌的请求(这可能意味着你点击了两次,而另一次收费仍然是通过(:tok_1DiK0uKIdjSiVG8mn8CV2iim。此令牌不能如果充电成功,请再次使用。"在/home/rbfs6nkk73qi/api/prototype/vender/cartalyst/stripe/src/Exception/Handler.php:123([堆栈跟踪]

〔2018-12-17 11:12:00〕当地。调试:交付订单

〔2018-12-17 11:12:00〕当地。调试:检查单个项目

〔2018-12-17 11:12:00〕当地。调试:卡交付-订单通过安卓V0.5应用

这表明,从我列出的步骤1-5,API做到了:1,2,1,2,error,3,4,5。

我完全不明白为什么我的API会随机运行两次。我已经将API的相关部分粘贴到下面,看看是否有明显的错误。感谢在这方面的任何帮助。

我尝试过的最后一件事是:我尝试在创建$stripe电荷对象后直接将$token变量设置为null,这让我怀疑这是否是某种Cartalyst bug

if($card) {
$token = $request->token; //Stripe token
Log::debug("Token: ".$token); //Step 1
try {
Log::debug("Creating Stripe Charge"); //Step 2
$stripe->charges()->create([
'currency' => $currency,
'amount'   => $amount,
'source' => $token
]);
if(strcmp($delivery,"Delivery") == 0) {
Log::debug("Delivery order"); //Step 3
if($singleItems != null) {
Log::debug("Checking single items"); //Step 4
foreach($singleItems as $key => $value) {
$singleItem = DB::table('items')
->select('name','category','price')
->where('item_id', '=', $key)
->get();
foreach($singleItem as $item) {
DB::table('single_order_items')->insert(['order_number' => $id, 'item'=>$key, 'quantity'=>$value, 'name'=>$item->name, 'category'=>$item->category, 'price'=>$item->price]);
}
}
}
Log::debug("Card delivery - ".$description); //Step 5
return response("Order placed successfully", 201)
->header('Content-Type', 'text/plain');
}
} catch(CartalystStripeExceptionBadRequestException $e) {
//This exception will be thrown when the data sent through the request is mal formed.
$message = $e->getMessage();
Log::debug($message);
return response($message, 306)
->header('Content-Type', 'text/plain');
} catch(CartalystStripeExceptionUnauthorizedException $e) {
//This exception will be thrown if your Stripe API Key is incorrect.
$message = $e->getMessage();
Log::debug($message);
return response($message, 307)
->header('Content-Type', 'text/plain');
} catch(CartalystStripeExceptionInvalidRequestException $e) {
//This exception will be thrown whenever the request fails for some reason.
$message = $e->getMessage();
Log::debug($message);
return response($message, 308)
->header('Content-Type', 'text/plain');
} catch(CartalystStripeExceptionNotFoundException $e) {
//This exception will be thrown whenever a request results on a 404.
$message = $e->getMessage();
Log::debug($message);
return response($message, 309)
->header('Content-Type', 'text/plain');
} catch(CartalystStripeExceptionCardErrorException $e) {
//This exception will be thrown whenever the credit card is invalid.
$message = $e->getMessage();
Log::debug($message);
return response($message, 310)
->header('Content-Type', 'text/plain');
} catch(CartalystStripeExceptionServerErrorException $e) {
//This exception will be thrown whenever Stripe does something wrong.
$message = $e->getMessage();
Log::debug($message);
return response($message, 311)
->header('Content-Type', 'text/plain');
}
}

经过进一步调试,找到了解决方案。事实证明,我在Android端使用的名为Volley的HTTP库不止一次发送请求。

将Volley请求设置为0次重试似乎解决了问题:

MyStringRequest.setRetryPolicy(new DefaultRetryPolicy(0,DefaultRetryPolicy.DEFAULT_MAX_RETRIES,DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));

最新更新