Странная ошибка Laravel / Stripe / Cartalyst, когда заряд случайно запускается дважды, используя один и тот же токен Stripe - PullRequest
0 голосов
/ 17 декабря 2018

Я создал мобильное приложение для Android, в которое встроен Stripe для приема карточных платежей.В моем приложении для Android я отправляю в свой Laravel API набор параметров, одним из которых является токен Stripe.

9 раз из 10, мой сервер обрабатывает это, как и ожидалось.Он создаст объект начисления с помощью Stripe-Cartalyst и создаст начисление, при условии, что не будет выброшено никаких исключений (таких как неверные данные карты или ошибка сервера), затем мой API выполнит обработку платежа как успешную и выполнит некоторые вставки вБД, после которой он возвращает код состояния HTTP 201 обратно клиенту, где я затем обрабатываю его на стороне 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-запрос был запущен только один раз, поэтому я не думаю, что это проблема с Android.

Далее, в файле журнала API произошло следующее:

2018-12-17 11:11:56] local.DEBUG: Токен: tok_1DiK0uKIdjSiVG8mn8CV2iim [2018-12-17 11:11:56] local.DEBUG: Создание заряда полосы

[2018-12-17 11:11:59] local.DEBUG: токен: tok_1DiK0uKIdjSiVG8mn8CV2iim

[2018-12-17 11:11:59] local.DEBUG: создание заряда полосы

[2018-12-17 11:12:00] local.ERROR: В настоящее время существует другой незавершенный запрос, использующий этот токен Stripe (это, вероятно, означает, что вы щелкнули дважды, а другой заряд все еще выполняется): tok_1DiK0uKIdjSiVG8mn8CV2iim.Этот токен не может быть использован снова, если этот заряд успешен.{"исключение": "[объект] (Cartalyst \ Stripe \ Exception \ MissingParameterException (код: 400): в настоящее время существует другой обрабатываемый запрос, использующий этот токен Stripe (это, вероятно, означает, что вы щелкнули дважды, а другой платеж все еще идетчерез): tok_1DiK0uKIdjSiVG8mn8CV2iim. Этот токен не может быть использован повторно, если этот заряд успешно выполнен.*

[2018-12-17 11:12:00] local.DEBUG: заказ на доставку

[2018-12-17 11:12:00] local.DEBUG: проверка отдельных элементов

[2018-12-17 11:12:00] local.DEBUG: Доставка карты - заказ размещен через приложение Android V0.5

Это показывает, что из перечисленных шагов 1-5, API сделал: 1,2,1,2, ошибка, 3,4,5.

Я в полной растерянности относительно того, почему мой API запускался случайным образом дважды. Я вставил соответствующуючасти моего API ниже, чтобы увидеть, если что-то явно не так, что я делаю. Оцените любую помощь по этому вопросу.

Последнее, что я попробовал: я попытался установить переменную $token равной null непосредственно после создания объекта заряда $stripe, что заставляет меня задаться вопросом, является ли это 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(\Cartalyst\Stripe\Exception\BadRequestException $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(\Cartalyst\Stripe\Exception\UnauthorizedException $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(\Cartalyst\Stripe\Exception\InvalidRequestException $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(\Cartalyst\Stripe\Exception\NotFoundException $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(\Cartalyst\Stripe\Exception\CardErrorException $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(\Cartalyst\Stripe\Exception\ServerErrorException $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');
            }
}

1 Ответ

0 голосов
/ 18 декабря 2018

После дальнейшей отладки решение было найдено.Оказывается, HTTP-библиотека, которую я использую на стороне Android под названием Volley, отправляла запрос более одного раза.

Установка запроса на залп на 0 попыток, кажется, решила проблему:

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