From a808210b3afd866d72dfe35fe33fb0fca2a37bce Mon Sep 17 00:00:00 2001 From: Djery-Tom Date: Mon, 14 Aug 2023 13:01:40 +0100 Subject: [PATCH] feat: handle Stripe errors messages --- app/Exceptions/Handler.php | 43 +++ app/Http/Controllers/StripeController.php | 374 ++++++++++------------ resources/lang/en/stripe.php | 56 ++++ resources/lang/fr/stripe.php | 56 ++++ 4 files changed, 330 insertions(+), 199 deletions(-) create mode 100644 resources/lang/en/stripe.php create mode 100644 resources/lang/fr/stripe.php diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index e243929..2963139 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -12,6 +12,7 @@ use Illuminate\Auth\AuthenticationException; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\QueryException; use Illuminate\Http\Response; +use Illuminate\Support\Facades\Lang; use Illuminate\Support\Facades\Log; use Illuminate\Validation\ValidationException; use Laravel\Lumen\Exceptions\Handler as ExceptionHandler; @@ -32,6 +33,7 @@ class Handler extends ExceptionHandler HttpException::class, ModelNotFoundException::class, ValidationException::class, + \Stripe\Exception\ApiErrorException::class, ]; /** @@ -120,6 +122,47 @@ class Handler extends ExceptionHandler return $this->errorResponse(trans('errors.timeout'), Response::HTTP_REQUEST_TIMEOUT); } + //Stripe Exceptions + //https://stripe.com/docs/api/errors/handling?lang=php +// if($exception instanceof \Stripe\Exception\CardException){ +// // Since it's a decline, \Stripe\Exception\CardException will be caught +// echo 'Status is:' . $exception->getHttpStatus() . '\n'; +// echo 'Type is:' . $exception->getError()->type . '\n'; +// echo 'Code is:' . $exception->getError()->code . '\n'; +// // param is '' in this case +// echo 'Param is:' . $exception->getError()->param . '\n'; +// echo 'Message is:' . $exception->getError()->message . '\n'; +// +// } +// +// if($exception instanceof \Stripe\Exception\RateLimitException){ +// } +// +// if($exception instanceof \Stripe\Exception\InvalidRequestException){ +// } +// +// if($exception instanceof \Stripe\Exception\AuthenticationException){ +// } +// +// if($exception instanceof \Stripe\Exception\ApiConnectionException){ +// } + + if($exception instanceof \Stripe\Exception\ApiErrorException){ + $errorMessage = $exception->getMessage(); + Log::error("Error Stripe API"); + + //https://stripe.com/docs/error-codes + //https://stripe.com/docs/declines/codes + $code = $exception->getError()->decline_code ?? $exception->getError()->code; + if(Lang::has('stripe.'.$code)){ + Log::error($code); + $errorMessage = __('stripe.'.$code); + } + + Log::error($errorMessage); + return $this->errorResponse($errorMessage ?? __('errors.unexpected_error')); + } + // if ($exception instanceof AppException) { // return $this->errorResponse($exception->getMessage(), $exception->getCode()); // } diff --git a/app/Http/Controllers/StripeController.php b/app/Http/Controllers/StripeController.php index 8c2f935..491ab37 100644 --- a/app/Http/Controllers/StripeController.php +++ b/app/Http/Controllers/StripeController.php @@ -199,35 +199,30 @@ class StripeController extends Controller $transaction = PaymentTransaction::where('transaction_id', $request->input('transaction_id'))->where('status', PaymentTransactionStatus::ACCEPTED)->firstOrFail(); $reason = $request->input('reason','requested_by_customer'); - try{ - $reference = $transaction->aggregator_payment_ref; - $twoFirstChars = substr($reference,0,2); - $keyToRefund = $twoFirstChars == 'ch' ? 'charge' : 'payment_intent'; - $refund = $this->client->refunds->create([ - $keyToRefund => $reference, - 'reason' => $reason + + $reference = $transaction->aggregator_payment_ref; + $twoFirstChars = substr($reference,0,2); + $keyToRefund = $twoFirstChars == 'ch' ? 'charge' : 'payment_intent'; + $refund = $this->client->refunds->create([ + $keyToRefund => $reference, + 'reason' => $reason + ]); + + if(!empty($refund)){ + PaymentRefund::create([ + 'refund_id' => $this->getTransactionID('payment_refunds'), + 'transaction_id' => $transaction->id, + "currency" => strtoupper($refund->currency), + "amount" => $refund->amount, + "reason" => $reason, + 'date' => date('Y-m-d H:i:s', $refund->created), + 'status' => strtoupper($refund->status) ]); - if(!empty($refund)){ - PaymentRefund::create([ - 'refund_id' => $this->getTransactionID('payment_refunds'), - 'transaction_id' => $transaction->id, - "currency" => strtoupper($refund->currency), - "amount" => $refund->amount, - "reason" => $reason, - 'date' => date('Y-m-d H:i:s', $refund->created), - 'status' => strtoupper($refund->status) - ]); - - return $this->successResponse("Payment refunded successfully"); - } - - }catch (Throwable $e){ - Log::error("Error Stripe refund"); - $errorMessage = $e->getMessage(); - Log::error($errorMessage); + return $this->successResponse("Payment refunded successfully"); } + return $this->errorResponse($errorMessage ?? __('errors.unexpected_error')); } @@ -260,101 +255,92 @@ class StripeController extends Controller $aggregator = PaymentAggregator::where('name','like','%stripe%')->firstOrFail(); - try{ + $amount = $request->input('amount'); + $currency = $request->input('currency'); - $amount = $request->input('amount'); - $currency = $request->input('currency'); - - if($amount < 325 && $currency == 'XAF'){ - $amount = 325; - } - - - $transaction = PaymentTransaction::create([ - 'aggregator_id' => $aggregator->id, - "currency" => $request->input('currency'), - "transaction_id" => $this->getTransactionID(), - "amount" => $amount, - "payment_method" => 'CARD', - 'status' => PaymentTransactionStatus::INITIATED, - "reason" => $request->input('reason'), - "customer_id" => $request->input('customer_id'), - "customer_name" => $request->input('customer_name'), - "customer_surname" => $request->input('customer_surname'), - "customer_email" => $request->input('customer_email'), - "customer_phone_number" => $request->input('customer_phone_number'), - "customer_address" => $request->input('customer_address'), - "customer_city" => $request->input('customer_city'), - "customer_country" => $request->input('customer_country'), - "customer_state" => $request->input('customer_state'), - "customer_zip_code" => $request->input('customer_zip_code'), - ]); - - // Create payment method in Stripe - // https://stripe.com/docs/api/payment_methods/create - $method = $this->client->paymentMethods->create([ - 'type' => 'card', - 'card' => [ - 'number' => $request->input('card_no'), - 'exp_month' => $request->input('exp_month'), - 'exp_year' => $request->input('exp_year'), - 'cvc' => $request->input('cvc'), - ] - ]); - - if($method){ - - $transaction->update([ - 'payment_method_exact' => $method->id - ]); - - - // Create payment intent - // https://stripe.com/docs/api/payment_intents/create - $paymentIntent = $this->client->paymentIntents->create([ - "amount" => stripeFormatCurrency($amount, $request->input('currency')), - "currency" => $request->input('currency'), - "description" => $request->input('reason'), - 'payment_method_types' => ['card'], - 'payment_method' => $method->id, - 'confirm' => 'true', - 'capture_method' => 'automatic', - 'return_url' => route('stripe.webhook',['transaction_id' => $transaction->transaction_id]), // Redirect url if 3d secure required - 'payment_method_options' => [ - 'card' => [ - 'request_three_d_secure' => 'automatic' - ] - ], - ]); - - - if($paymentIntent){ - - $transaction->update([ - 'payment_date' => date('Y-m-d H:i:s', $paymentIntent->created), - 'aggregator_payment_ref' => $paymentIntent->id - ]); - - if(!empty($paymentIntent->next_action->redirect_to_url->url)){ - return $this->successResponse([ - 'message' => '3D secure redirect', - 'payment_url' => $paymentIntent->next_action->redirect_to_url->url - ], ResponseAlias::HTTP_MOVED_PERMANENTLY); - } - - return $this->handlePaymentIntentResult($transaction, $paymentIntent); - } - - } - - - }catch (Throwable $e){ - Log::error("Error Stripe submit payment intent"); - $errorMessage = $e->getMessage(); - Log::error($errorMessage); + if($amount < 325 && $currency == 'XAF'){ + $amount = 325; } - return $this->errorResponse($errorMessage ?? __('errors.unexpected_error')); + + $transaction = PaymentTransaction::create([ + 'aggregator_id' => $aggregator->id, + "currency" => $request->input('currency'), + "transaction_id" => $this->getTransactionID(), + "amount" => $amount, + "payment_method" => 'CARD', + 'status' => PaymentTransactionStatus::INITIATED, + "reason" => $request->input('reason'), + "customer_id" => $request->input('customer_id'), + "customer_name" => $request->input('customer_name'), + "customer_surname" => $request->input('customer_surname'), + "customer_email" => $request->input('customer_email'), + "customer_phone_number" => $request->input('customer_phone_number'), + "customer_address" => $request->input('customer_address'), + "customer_city" => $request->input('customer_city'), + "customer_country" => $request->input('customer_country'), + "customer_state" => $request->input('customer_state'), + "customer_zip_code" => $request->input('customer_zip_code'), + ]); + + // Create payment method in Stripe + // https://stripe.com/docs/api/payment_methods/create + $method = $this->client->paymentMethods->create([ + 'type' => 'card', + 'card' => [ + 'number' => $request->input('card_no'), + 'exp_month' => $request->input('exp_month'), + 'exp_year' => $request->input('exp_year'), + 'cvc' => $request->input('cvc'), + ] + ]); + + if($method){ + + $transaction->update([ + 'payment_method_exact' => $method->id + ]); + + + // Create payment intent + // https://stripe.com/docs/api/payment_intents/create + $paymentIntent = $this->client->paymentIntents->create([ + "amount" => stripeFormatCurrency($amount, $request->input('currency')), + "currency" => $request->input('currency'), + "description" => $request->input('reason'), + 'payment_method_types' => ['card'], + 'payment_method' => $method->id, + 'confirm' => 'true', + 'capture_method' => 'automatic', + 'return_url' => route('stripe.webhook',['transaction_id' => $transaction->transaction_id]), // Redirect url if 3d secure required + 'payment_method_options' => [ + 'card' => [ + 'request_three_d_secure' => 'automatic' + ] + ], + ]); + + + if($paymentIntent){ + + $transaction->update([ + 'payment_date' => date('Y-m-d H:i:s', $paymentIntent->created), + 'aggregator_payment_ref' => $paymentIntent->id + ]); + + if(!empty($paymentIntent->next_action->redirect_to_url->url)){ + return $this->successResponse([ + 'message' => '3D secure redirect', + 'payment_url' => $paymentIntent->next_action->redirect_to_url->url + ], ResponseAlias::HTTP_MOVED_PERMANENTLY); + } + + return $this->handlePaymentIntentResult($transaction, $paymentIntent); + } + + } + + return $this->errorResponse(__('errors.unexpected_error')); } @@ -388,99 +374,89 @@ class StripeController extends Controller $aggregator = PaymentAggregator::where('name','like','%stripe%')->firstOrFail(); - try{ + $amount = $request->input('amount'); + $currency = $request->input('currency'); + $payment_method = $request->input('payment_method'); - $amount = $request->input('amount'); - $currency = $request->input('currency'); - $payment_method = $request->input('payment_method'); + $transaction = PaymentTransaction::create([ + 'aggregator_id' => $aggregator->id, + "currency" => $request->input('currency'), + "transaction_id" => $this->getTransactionID(), + "amount" => $amount, + "payment_method" => $payment_method, + 'status' => PaymentTransactionStatus::INITIATED, + "reason" => $request->input('reason'), + "customer_id" => $request->input('customer_id'), + "customer_name" => $request->input('customer_name'), + "customer_surname" => $request->input('customer_surname'), + "customer_email" => $request->input('customer_email'), + "customer_phone_number" => $request->input('customer_phone_number'), + "customer_address" => $request->input('customer_address'), + "customer_city" => $request->input('customer_city'), + "customer_country" => $request->input('customer_country'), + "customer_state" => $request->input('customer_state'), + "customer_zip_code" => $request->input('customer_zip_code'), + ]); - $transaction = PaymentTransaction::create([ - 'aggregator_id' => $aggregator->id, - "currency" => $request->input('currency'), - "transaction_id" => $this->getTransactionID(), - "amount" => $amount, - "payment_method" => $payment_method, - 'status' => PaymentTransactionStatus::INITIATED, - "reason" => $request->input('reason'), - "customer_id" => $request->input('customer_id'), - "customer_name" => $request->input('customer_name'), - "customer_surname" => $request->input('customer_surname'), - "customer_email" => $request->input('customer_email'), - "customer_phone_number" => $request->input('customer_phone_number'), - "customer_address" => $request->input('customer_address'), - "customer_city" => $request->input('customer_city'), - "customer_country" => $request->input('customer_country'), - "customer_state" => $request->input('customer_state'), - "customer_zip_code" => $request->input('customer_zip_code'), + // Create destination account in Stripe + + if($payment_method == 'CARD'){ + //https://stripe.com/docs/api/external_account_cards/create + + $destination = $this->client->accounts->createExternalAccount( + config('variables.stripe_account'), [ + 'external_account' => [ + 'object' => 'card', + 'number' => $request->input('card_no'), + 'exp_month' => $request->input('exp_month'), + 'exp_year' => $request->input('exp_year'), +// 'cvc' => $request->input('cvc'), + ] + ] + ); + }else{ + //https://stripe.com/docs/api/external_account_bank_accounts/create#account_create_bank_account + $destination = $this->client->accounts->createExternalAccount( + config('variables.stripe_account'), [ + 'external_account' => [ + 'object' => 'bank_account', + 'country' => $request->input('bank_country'), + 'currency' => $request->input('bank_currency'), + 'account_number' => $request->input('bank_account_number'), + ] + ] + ); + } + + if($destination){ + + $transaction->update([ + 'payment_method_exact' => $destination->id ]); - // Create destination account in Stripe + // Create payout in Stripe + // https://stripe.com/docs/api/payouts/create + $payout = $this->client->payouts->create([ + "amount" => stripeFormatCurrency($amount, $currency), + "currency" => $currency, + 'description' => $request->input('reason'), + "destination" => $destination->id, + "method" => $payment_method == 'CARD' ? 'instant' : 'standard' + ]); - if($payment_method == 'CARD'){ - //https://stripe.com/docs/api/external_account_cards/create - - $destination = $this->client->accounts->createExternalAccount( - config('variables.stripe_account'), [ - 'external_account' => [ - 'object' => 'card', - 'number' => $request->input('card_no'), - 'exp_month' => $request->input('exp_month'), - 'exp_year' => $request->input('exp_year'), -// 'cvc' => $request->input('cvc'), - ] - ] - ); - }else{ - //https://stripe.com/docs/api/external_account_bank_accounts/create#account_create_bank_account - $destination = $this->client->accounts->createExternalAccount( - config('variables.stripe_account'), [ - 'external_account' => [ - 'object' => 'bank_account', - 'country' => $request->input('bank_country'), - 'currency' => $request->input('bank_currency'), - 'account_number' => $request->input('bank_account_number'), - ] - ] - ); - } - - if($destination){ + if($payout) { $transaction->update([ - 'payment_method_exact' => $destination->id + 'payment_date' => date('Y-m-d H:i:s', $payout->arrival_date), + 'aggregator_payment_ref' => $payout->id, + 'status' => strtolower($payout->status) ]); - // Create payout in Stripe - // https://stripe.com/docs/api/payouts/create - $payout = $this->client->payouts->create([ - "amount" => stripeFormatCurrency($amount, $currency), - "currency" => $currency, - 'description' => $request->input('reason'), - "destination" => $destination->id, - "method" => $payment_method == 'CARD' ? 'instant' : 'standard' + return $this->successResponse([ + 'transaction_id' => $transaction->transaction_id, + 'status' => $transaction->status ]); - - if($payout) { - - $transaction->update([ - 'payment_date' => date('Y-m-d H:i:s', $payout->arrival_date), - 'aggregator_payment_ref' => $payout->id, - 'status' => strtolower($payout->status) - ]); - - return $this->successResponse([ - 'transaction_id' => $transaction->transaction_id, - 'status' => $transaction->status - ]); - } } - - - - }catch (Throwable $e){ - Log::error("Error Stripe submit payment intent"); - $errorMessage = $e->getMessage(); - Log::error($errorMessage); } return $this->errorResponse($errorMessage ?? __('errors.unexpected_error')); diff --git a/resources/lang/en/stripe.php b/resources/lang/en/stripe.php new file mode 100644 index 0000000..891c035 --- /dev/null +++ b/resources/lang/en/stripe.php @@ -0,0 +1,56 @@ + "The customer's bank account has been closed", + "account_already_exists" => "Account with that email already exists", + "account_country_invalid_address" => "The country of the account or business address is invalid for the account's country", + "account_error_country_change_requires_additional_steps" => "Your account has already onboarded as a Connect platform", + "account_information_mismatch" => "Account information mismatches with one another", + "account_invalid" => "Invalid account ID", + "account_number_invalid" => "The bank account number provided is invalid (e.g., missing digits)", + "amount_too_large" => "The specified amount is greater than the maximum amount allowed. Use a lower amount and try again", + "amount_too_small" => "The specified amount is less than the minimum amount allowed. Use a higher amount and try again", + "api_key_expired" => "The API key provided has expired", + "authentication_required" => "Authentication required for this action", + "balance_insufficient" => "Insufficient balance to perform the requested action", + "bank_account_bad_routing_numbers" => "The bank account is known to not support the currency in question", + "bank_account_declined" => "The bank account provided can not be used to charge, either because it is not verified yet or it is not supported", + "debit_not_authorized" => "The customer has notified their bank that this payment was unauthorized.", + "card_declined" => "The card has been declined", + "card_decline_rate_limit_exceeded" => "This card has been declined too many times. You can try to charge this card again after 24 hours", + "cardholder_phone_number_required" => "You must have a phone number on file for Issuing Cardholders who will be creating EU cards", + "expired_card" => "The card has expired. Check the expiration date or use a different card.", + "incorrect_cvc" => "The card's security code is incorrect. Check the card's address or use a different card.", + "incorrect_address" => "The card's address is incorrect. Check the card's address or use a different card.", + "incorrect_zip" => "The card's postal code is incorrect. Check the card's postal code or use a different card.", + "insufficient_funds" => "The customer's account has insufficient funds to cover this payment.", + "invalid_charge_amount" => "The specified amount is invalid", + "invalid_cvc" => "The card's security code is invalid. Check the card's security code or use a different card.", + "invalid_expiry_month" => "The card's expiration month is incorrect. Check the expiration date or use a different card.", + "invalid_expiry_year" => "The card's expiration year is incorrect. Check the expiration date or use a different card.", + "invalid_number" => "The card number is invalid. Check the card details or use a different card.", + "processing_error" => "An error occurred while processing the card. Try again later or with a different payment method.", + "generic_decline" => "The card has been declined for an unknown reason", + "lost_card" => "Payment has been declined, as the bank card has been reported lost.", + "merchant_blacklist" => "The payment has been refused, because it matches an entry in the blocklist", + "not_permitted" => "The payment is not authorized", + "try_again_later" => "The card has been refused for an unknown reason.", + "stolen_card" => "Payment has been refused because the bank card has been reported stolen.", + "pin_try_exceeded" => "The number of authorized attempts to enter the PIN code has been exceeded.", + "offline_pin_required" => "The card has been refused, as it requires a PIN code.", + "online_or_offline_pin_required" => "The card has been refused because it requires a PIN code.", + "fraudulent" => "The payment has been refused because it has been identified as potentially fraudulent.", + "currency_not_supported" => "The specified currency is not supported by this bank card.", + "card_velocity_exceeded" => "The customer has exceeded the balance or credit limit available on their bank card.", + "card_not_supported" => "This type of payment is not supported by this bank card.", + "call_issuer" => "The card has been declined for an unknown reason.", + "approve_with_id" => "It is not possible to authorize the payment", + "duplicate_transaction" => "A transaction for the same amount with the same credit card information was submitted just recently.", + "issuer_not_available" => "The card issuer cannot be reached, so the payment cannot be authorized.", + "new_account_information_available" => "The bank card, or the account to which it is connected, is invalid.", + "no_action_taken" => "The card has been declined for an unknown reason.", + "pickup_card" => "The customer cannot use this card to make this payment (it may have been reported lost or stolen).", + "withdrawal_count_limit_exceeded" => "The customer has exceeded the balance or credit limit available on his bank card.", + "transaction_not_allowed" => "The card has been refused for an unknown reason.", + "testmode_decline" => "The card used is a test card" +]; diff --git a/resources/lang/fr/stripe.php b/resources/lang/fr/stripe.php new file mode 100644 index 0000000..dce087f --- /dev/null +++ b/resources/lang/fr/stripe.php @@ -0,0 +1,56 @@ + "Le compte bancaire du client a été clôturé", + "account_already_exists" => "Le compte avec cet email existe déjà", + "account_country_invalid_address" => "Le pays de l'adresse du compte ou de l'entreprise n'est pas valide pour le pays du compte", + "account_error_country_requires_additional_steps" => "Your account has already onboarded as a Connect platform", + "account_information_mismatch" => "Les informations du compte ne correspondent pas entre elles", + "account_invalid" => "ID de compte invalide", + "account_number_invalid" => "Le numéro de compte bancaire fourni n'est pas valide (par exemple, chiffres manquants)", + "amount_too_large" => "Le montant spécifié est supérieur au montant maximum autorisé. Utilisez un montant inférieur et réessayez", + "amount_too_small" => "Le montant spécifié est inférieur au montant minimum autorisé. Utilisez un montant plus élevé et réessayez", + "api_key_expired" => "La clé API fournie a expiré", + "authentication_required" => "Authentification requise pour cette action", + "balance_insufficient" => "Solde insuffisant pour effectuer l'action demandée", + "bank_account_bad_routing_numbers" => "Le compte bancaire est connu pour ne pas supporter la devise en question", + "bank_account_declined" => "Le compte bancaire fourni ne peut pas être utilisé pour débiter, soit parce qu'il n'est pas encore vérifié, soit parce qu'il n'est pas pris en charge", + "debit_not_authorized" => "Le client a notifié à sa banque que ce paiement n'était pas autorisé", + "card_declined" => "La carte a été refusée", + "card_decline_rate_limit_exceeded" => "Cette carte a été refusée trop souvent. Vous pouvez essayer de recharger cette carte après 24 heures", + "cardholder_phone_number_required" => "Vous devez disposer d'un numéro de téléphone pour les titulaires de cartes qui créeront des cartes UE", + "expired_card" => "La carte a expiré. Vérifiez la date d'expiration ou utilisez une autre carte", + "incorrect_cvc" => "Le code de sécurité de la carte est incorrect. Vérifiez l'adresse de la carte ou utilisez une autre carte", + "incorrect_address" => "L'adresse de la carte est incorrecte. Vérifiez l'adresse de la carte ou utilisez une autre carte.", + "incorrect_zip" => "Le code postal de la carte est incorrect. Vérifiez le code postal de la carte ou utilisez une autre carte.", + "insufficient_funds" => "Le compte du client n'est pas suffisamment approvisionné pour couvrir ce paiement", + "invalid_charge_amount" => "Le montant spécifié n'est pas valide", + "invalid_cvc" => "Le code de sécurité de la carte n'est pas valide. Vérifiez le code de sécurité de la carte ou utilisez une autre carte", + "invalid_expiry_month" => "Le mois d'expiration de la carte est incorrect. Vérifiez la date d'expiration ou utilisez une autre carte.", + "invalid_expiry_year" => "L'année d'expiration de la carte est incorrecte. Vérifiez la date d'expiration ou utilisez une autre carte.", + "invalid_number" => "Le numéro de la carte n'est pas valide. Vérifiez les détails de la carte ou utilisez une autre carte.", + "processing_error" => "Une erreur s'est produite lors du traitement de la carte. Réessayez plus tard ou avec une autre méthode de paiement.", + "generic_decline" => "La carte a été refusée pour une raison inconnue", + "lost_card" => "Le paiement a été refusé, car la carte bancaire a été déclarée perdue.", + "merchant_blacklist" => "Le paiement a été refusé, car il correspond à une entrée de la liste de blocage", + "not_permitted" => "Le paiement n'est pas autorisé.", + "try_again_later" => "La carte a été refusée pour une raison inconnue.", + "stolen_card" => "Le paiement a été refusé, car la carte bancaire a été déclarée volée.", + "pin_try_exceeded" => "Le nombre de tentatives autorisées de saisie du code PIN a été dépassé.", + "offline_pin_required" => "La carte a été refusée, car elle nécessite un code PIN.", + "online_or_offline_pin_required" => "La carte a été refusée, car elle nécessite un code PIN.", + "fraudulent" => "Le paiement a été refusé car identifié comme potentiellement frauduleux.", + "currency_not_supported" => "La devise spécifiée n'est pas prise en charge par cette carte bancaire.", + "card_velocity_exceeded" => "Le client a dépassé le solde ou la limite de crédit disponible sur sa carte bancaire.", + "card_not_supported" => "Ce type de paiement n'est pas pris en charge par cette carte bancaire.", + "call_issuer" => "La carte a été refusée pour une raison inconnue.", + "approve_with_id" => "Il n'est pas possible d'autoriser le paiement", + "duplicate_transaction" => "Une transaction du même montant avec les mêmes informations de carte bancaire a été soumise tout récemment.", + "issuer_not_available" => "Il n'est pas possible de joindre l'émetteur de la carte, donc d'autoriser le paiement.", + "new_account_information_available" => "La carte bancaire, ou le compte auquel elle est connectée, n'est pas valide.", + "no_action_taken" => "La carte a été refusée pour une raison inconnue.", + "pickup_card" => "Le client ne peut pas utiliser cette carte pour effectuer ce paiement (il est possible qu'elle ait été déclarée perdue ou volée).", + "withdrawal_count_limit_exceeded" => "Le client a dépassé le solde ou la limite de crédit disponible sur sa carte bancaire.", + "transaction_not_allowed" => "La carte a été refusée pour une raison inconnue.", + "testmode_decline" => "La carte utilisée est une carte de test" +];