Intégration des méthodes de paiement Stripe et PayPal dans Ruby on Rails
Publié: 2022-03-11Une caractéristique clé pour les grandes entreprises de commerce électronique telles qu'AliExpress, Ebay et Amazon est un moyen sécurisé de gérer les paiements, ce qui est essentiel pour leur entreprise. Si cette fonctionnalité échoue, les conséquences seraient dévastatrices. Cela s'applique aux leaders de l'industrie et aux développeurs Ruby on Rails travaillant sur des applications de commerce électronique.
La cybersécurité est essentielle pour prévenir les attaques, et un moyen de rendre le processus de transaction plus sûr consiste à demander à un service tiers de le gérer. L'inclusion de passerelles de paiement dans votre application est un moyen d'atteindre cet objectif, car elles fournissent l'autorisation de l'utilisateur, le cryptage des données et un tableau de bord afin que vous puissiez suivre l'état de la transaction à la volée.
Il existe une variété de services de passerelle de paiement sur le Web, mais dans cet article, je me concentrerai sur l'intégration de Stripe et PayPal à une application Rails. Pour n'en citer que quelques-uns : Amazon Payments, Square, SecurePay, WorldPay, Authorize.Net, 2Checkout.com, Braintree, Amazon ou BlueSnap.
Comment fonctionne l'intégration de la passerelle de paiement

En général, il y aura un formulaire/bouton dans votre application où l'utilisateur pourra se connecter/insérer des données de carte de crédit. PayPal et Stripe rendent déjà cette première étape plus sûre en utilisant des formulaires iframe
ou des popups
qui empêchent votre application de stocker des informations sensibles sur la carte de crédit de l'utilisateur car ils renverront un jeton représentant cette transaction. Certains utilisateurs peuvent également se sentir plus en confiance pour traiter les paiements en sachant qu'un service tiers gère le processus de transaction, ce qui peut également être une attraction pour votre application.
Après avoir authentifié les informations de l'utilisateur, une passerelle de paiement confirmera le paiement en contactant un processeur de paiement qui communique avec les banques afin de régler les paiements. Cela garantit que la transaction est correctement débitée/créditée.
Stripe utilise un formulaire de carte de crédit demandant le numéro de carte de crédit, le cvv et la date d'expiration. L'utilisateur doit donc remplir les informations de carte de crédit dans les entrées sécurisées de Stripe. Après avoir fourni ces informations, le backend de votre application traite ce paiement via un jeton.
Contrairement à Stripe, PayPal redirige l'utilisateur vers la page de connexion PayPal. L'utilisateur autorise et sélectionne le mode de paiement via PayPal, et encore une fois, votre back-end gérera les jetons au lieu des données sensibles de l'utilisateur.
Il est important de mentionner que, pour ces deux passerelles de paiement, votre back-end doit demander la poursuite de l'exécution de la transaction via les API Stripe ou PayPal qui donneront une réponse OK/NOK, de sorte que votre application doit rediriger l'utilisateur vers une page de réussite ou d'erreur en conséquence.
Le but de cet article est de fournir un guide rapide pour intégrer ces deux passerelles de paiement dans une seule application. Pour tous les tests, nous utiliserons des bacs à sable et des comptes de test fournis par Stripe et PayPal afin de simuler les paiements.
Installer
Avant d'intégrer les passerelles de paiement, nous allons faire une configuration pour initialiser l'application en ajoutant des gemmes, des tables de base de données et une page d'index. Ce projet a été créé avec Rails version 5.2.3 et Ruby 2.6.3.
Remarque : vous pouvez découvrir les nouvelles fonctionnalités de Rails 6 dans notre récent article.
Étape 1 : Initialiser une application Rails.
Initialisez le projet en exécutant l'initialisation du projet avec la commande rails
avec le nom de votre application :
rails new YOUR_APP_NAME
Et cd
dans votre dossier d'application.
Étape 2 : Installez les gemmes.
Outre les gemmes Stripe et PayPal, quelques autres gemmes ont été ajoutées :
-
devise
: utilisé pour l'authentification et l'autorisation des utilisateurs -
haml
: outil de création de modèles pour le rendu des pages utilisateur -
jquery-rails
: pourjquery
dans les scripts frontaux -
money-rails
: pour afficher les valeurs monétaires formatées
Ajoutez à votre Gemfile
:
gem "devise", ">= 4.7.1" gem "haml" gem "jquery-rails" gem "money-rails"
Après l'avoir ajouté, exécutez-le dans votre CLI :
bundle install
Étape 3 : Initialisez les gemmes.
Certaines de ces gemmes nécessiteront une initialisation en plus de leur installation via bundle
.
Dispositif d'installation :
rails g devise:install
Initialisation money-rails
:
rails g money_rails:initializer
Initialisez jquery-rails
en ajoutant au bas de app/assets/javascripts/application.js
ce qui suit :
//= require jquery //= require jquery_ujs
Étape 4 : Tables et migrations
Trois tables seront utilisées dans ce projet Users , Products et Orders .
-
Users
: Seront générés par devis - Colonnes
Products
:-
name
-
price_cents
-
Stripe_plan_name
: un ID représentant un plan d'abonnement créé dans Stripe, afin que les utilisateurs puissent s'y abonner. Ce champ n'est requis que pour les produits associés à un plan Stripe. -
paypal_plan_name
: Identique àstripe_plan_name
mais pour PayPal
-
- Colonnes
Orders
:-
product_id
-
user_id
-
status
: Cela indiquera si la commande est en attente, échouée ou payée. -
token
: Il s'agit d'un token généré à partir des API (soit Stripe soit PayPal) afin d'initialiser une transaction. -
price_cents
: Similaire au produit, mais utilisé pour rendre cette valeur persistante dans l'enregistrement de la commande -
payment_gateway
: Enregistre la passerelle de paiement utilisée pour la commande PayPal ou Stripe -
customer_id
: Ceci sera utilisé pour Stripe afin de stocker le client Stripe pour un abonnement, et cela sera expliqué plus en détail dans une section ultérieure.
-
Pour générer ces tables, quelques migrations doivent être générées :
Pour créer la table Utilisateurs . Cours:
rails g devise User
Pour créer la table Produits . Générez une migration en exécutant :
rails generate migration CreateProducts name:string stripe_plan_name:string paypal_plan_name:string
Ouvrez votre fichier de migration créé, qui doit se trouver dans db/migrate/
, et apportez des modifications pour que votre migration ressemble à ceci :
class CreateProducts < ActiveRecord::Migration[5.2] def change create_table :products do |t| t.string :name t.string :stripe_plan_name t.string :paypal_plan_name end add_money :products, :price, currency: { present: true } end end
Pour créer la table Commandes . Générez une migration en exécutant :
rails generate migration CreateOrders product_id:integer user_id:integer status:integer token:string charge_id:string error_message:string customer_id:string payment_gateway:integer
Encore une fois, ouvrez votre fichier de migration créé qui devrait être situé dans db/migrate/
et apportez des modifications à ce fichier afin qu'il ressemble à ceci :
class CreateOrders < ActiveRecord::Migration[5.2] def change create_table :orders do |t| t.integer :product_id t.integer :user_id t.integer :status, default: 0 t.string :token t.string :charge_id t.string :error_message t.string :customer_id t.integer :payment_gateway t.timestamps end add_money :orders, :price, currency: { present: false } end end
Exécutez les migrations de base de données en exécutant :
rails db:migrate
Étape 5 : Créer des modèles.
Le modèle utilisateur est déjà créé à partir de l'installation de l'outil et aucune modification ne sera nécessaire. En plus de cela, deux modèles seront créés pour Product et Order .
Produit. Ajoutez un nouveau fichier, app/models/product.rb
, avec :
class Product < ActiveRecord::Base monetize :price_cents has_many :orders end
Commande. Ajoutez un nouveau fichier, app/models/order.rb
, avec :
class Order < ApplicationRecord enum status: { pending: 0, failed: 1, paid: 2, paypal_executed: 3} enum payment_gateway: { stripe: 0, paypal: 1 } belongs_to :product belongs_to :user scope :recently_created, -> { where(created_at: 1.minutes.ago..DateTime.now) } def set_paid self.status = Order.statuses[:paid] end def set_failed self.status = Order.statuses[:failed] end def set_paypal_executed self.status = Order.statuses[:paypal_executed] end end
Étape 6 : Remplissez la base de données.
Un utilisateur et deux produits seront créés dans la console. Les enregistrements de commande seront créés en fonction des tests de paiement.
- Courir
rails s
- Dans votre navigateur, visitez
http://localhost:3000
- Vous serez redirigé vers une page d'inscription.
- Inscrivez un utilisateur en renseignant son adresse e-mail et son mot de passe.
- Dans votre terminal, les journaux suivants seront affichés indiquant qu'un utilisateur a été créé dans votre base de données :
User Create (0.1ms) INSERT INTO "users" ("email", "encrypted_password", "created_at", "updated_at") VALUES (?, ?, ?, ?) …
- Créez deux produits sans abonnement en exécutant les
rails c
et en ajoutant :-
Product.create(name: "Awesome T-Shirt", price_cents: 3000)
-
Product.create(name: "Awesome Sneakers", price_cents: 5000)
-
Étape 7 : Créer une page d'index
La page principale du projet comprend une sélection de produits pour les achats ou les abonnements. De plus, il comporte également une section pour la sélection du mode de paiement (Stripe ou PayPal). Un bouton d'envoi est également utilisé pour chaque type de passerelle de paiement, car pour PayPal, nous ajouterons sa propre conception de bouton via sa bibliothèque JavaScript.
Tout d'abord, créez les routes pour index
et submit
-les dans config/routes.rb
.
Rails.application.routes.draw do devise_for :users get '/', to: 'orders#index' post '/orders/submit', to: 'orders#submit' end
Créez et ajoutez des actions index
et submit
dans le contrôleur de commandes app/controllers/orders_controller.rb
. L'action orders#index
stocke deux variables à consommer dans le front-end : @products_purchase
qui a une liste de produits sans plans et @products_subscription
qui a des produits avec des plans PayPal et Stripe.
class OrdersController < ApplicationController before_action :authenticate_user! def index products = Product.all @products_purchase = products.where(stripe_plan_name:nil, paypal_plan_name:nil) @products_subscription = products - @products_purchase end def submit end end
Créez un fichier dans app/views/orders/index.html.haml
. Ce fichier contient toutes les entrées que nous allons envoyer à notre serveur principal via la méthode de soumission, ainsi que l'interaction pour les passerelles de paiement et la sélection de produits. Voici quelques attributs de nom d'entrée :
-
Orders[product_id]
stocke l'identifiant du produit. -
Orders[payment_gateway]
contient la passerelle de paiement avec les valeurs Stripe ou PayPal pour l'autre.
%div %h1 List of products = form_tag({:controller => "orders", :action => "submit" }, {:id => 'order-details'}) do %input{id:'order-type', :type=>"hidden", :value=>"stripe", :name=>'orders[payment_gateway]'} .form_row %h4 Charges/Payments - @products_purchase.each do |product| %div{'data-charges-and-payments-section': true} = radio_button_tag 'orders[product_id]', product.id, @products_purchase.first == product %span{id: "radioButtonName#{product.id}"} #{product.name} %span{id: "radioButtonPrice#{product.id}", :'data-price' => "#{product.price_cents}"} #{humanized_money_with_symbol product.price} %br %h4 Subscriptions - @products_subscription.each do |product| %div = radio_button_tag 'orders[product_id]', product.id, false %span{id: "radioButtonName#{product.id}"} #{product.name} %span{id: "radioButtonPrice#{product.id}", :'data-price' => "#{product.price_cents}"} #{humanized_money_with_symbol product.price} %br %hr %h1 Payment Method .form_row %div = radio_button_tag 'payment-selection', 'stripe', true, onclick: "changeTab();" %span Stripe %br %div = radio_button_tag 'payment-selection', 'paypal', false, onclick: "changeTab();" %span Paypal %br %br %div{id:'tab-stripe', class:'paymentSelectionTab active'} %div{id:'card-element'} %div{id:'card-errors', role:"alert"} %br %br = submit_tag "Buy it!", id: "submit-stripe" %div{id:'tab-paypal', class:'paymentSelectionTab'} %div{id: "submit-paypal"} %br %br %hr :javascript function changeTab() { var newActiveTabID = $('input[name="payment-selection"]:checked').val(); $('.paymentSelectionTab').removeClass('active'); $('#tab-' + newActiveTabID).addClass('active'); } :css #card-element { width:500px; } .paymentSelectionTab { display: none; } .paymentSelectionTab.active { display: block !important; }
Si vous exécutez votre application avec des rails s
et visitez votre page dans http://localhost:3000
. Vous devriez pouvoir voir la page comme suit :

Stockage des informations d'identification de la passerelle de paiement
Les clés PayPal et Stripe seront stockées dans un fichier non suivi par Git. Il existe deux types de clés stockées dans ce fichier pour chaque passerelle de paiement, et pour l'instant, nous utiliserons une valeur fictive pour elles. Des instructions supplémentaires pour la création de ces clés sont présentées dans les sections suivantes.
Étape 1 : Ajoutez ceci dans .gitignore
.
/config/application.yml
Étape 2 : Créez un fichier avec vos informations d'identification dans config/application.yml
. Il doit contenir toutes vos clés de bac à sable/test PayPal et Stripe pour accéder à ces API.
test: &default PAYPAL_ENV: sandbox PAYPAL_CLIENT_ID: YOUR_CREDENTIAL_HERE PAYPAL_CLIENT_SECRET: YOUR_CREDENTIAL_HERE STRIPE_PUBLISHABLE_KEY: YOUR_CREDENTIAL_HERE STRIPE_SECRET_KEY: YOUR_CREDENTIAL_HERE development: <<: *default
Étape 3 : Afin de stocker les variables du fichier config/application.yml
au démarrage de l'application, ajoutez ces lignes dans config/application.rb
à l'intérieur de la classe Application
afin qu'elles soient disponibles dans ENV
.
config_file = Rails.application.config_for(:application) config_file.each do |key,value| ENV[key] = value end unless config_file.nil?
Configuration de bande
Nous ajouterons une gemme pour utiliser l'API Stripe : stripe-rails
. La création d'un compte Stripe est également nécessaire pour que les frais et les abonnements puissent être traités. Si nécessaire, vous pouvez consulter les méthodes API pour l'API Stripe dans la documentation officielle.
Étape 1 : Ajoutez la gemme Stripe-Rails à votre projet.
La gemme stripe-rails fournira une interface pour toutes les requêtes API utilisées dans ce projet.
Ajoutez ceci dans le Gemfile
:
gem 'stripe-rails'
Cours:
bundle install
Étape 2 : Générez vos clés API.
Afin d'avoir les clés API pour communiquer avec Stripe, vous devrez créer un compte dans Stripe. Pour tester l'application, il est possible d'utiliser le mode test, donc aucune véritable information commerciale ne doit être renseignée dans le processus de création de compte Stripe.
- Créez un compte dans Stripe si vous n'en avez pas (https://dashboard.stripe.com/).
- Toujours dans le tableau de bord Stripe, après vous être connecté, activez Afficher les données de test .
- Sur https://dashboard.stripe.com/test/apikeys, remplacez
YOUR_CREDENTIAL_HERE
pour les valeursSTRIPE_PUBLISHABLE_KEY
etSTRIPE_SECRET_KEY
dans/config/application.yml
par le contenu dePublishable Key
etSecret key
.
Étape 3 : Initialiser le module Stripe
En plus de remplacer les clés, nous devons encore initialiser le module Stripe, afin qu'il utilise les clés déjà définies dans notre ENV
.
Créez un fichier dans config/initializers/stripe.rb
avec :
Rails.application.configure do config.stripe.secret_key = ENV["STRIPE_SECRET_KEY"] config.stripe.publishable_key = ENV["STRIPE_PUBLISHABLE_KEY"] end
Étape 4 : Intégrez Stripe dans le front-end.
Nous ajouterons la bibliothèque Stripe JavaScript et la logique d'envoi d'un jeton qui représente les informations de carte de crédit de l'utilisateur et sera traité dans notre back-end.
Dans le fichier index.html.haml
, ajoutez ceci en haut de votre fichier. Cela utilisera le module Stripe (fourni par la gemme) pour ajouter la bibliothèque javascript Stripe à la page de l'utilisateur.
= stripe_javascript_tag
Stripe utilise des champs de saisie sécurisés créés via leur API. Comme ils sont créés dans un iframe
créé via cette API, vous n'aurez pas à vous soucier des éventuelles vulnérabilités traitant les informations de carte de crédit de l'utilisateur. De plus, votre serveur principal ne sera pas en mesure de traiter/stocker les données sensibles de l'utilisateur, et il ne recevra qu'un jeton représentant ces informations.
Ces champs de saisie sont créés en appelant stripe.elements().create('card')
. Après cela, il suffit d'appeler l'objet renvoyé avec mount()
en passant comme argument l'élément HTML id/class où ces entrées doivent être montées. Plus d'informations peuvent être trouvées sur Stripe.
Lorsque l'utilisateur appuie sur le bouton d'envoi avec le mode de paiement Stripe, un autre appel d'API renvoyant une promesse est effectué sur l'élément de carte Stripe créé :
stripe.createToken(card).then(function(result)
La variable de result
de cette fonction, si aucune erreur de propriété n'est affectée, aura un jeton qui peut être récupéré en accédant à l'attribut result.token.id
. Ce jeton sera envoyé au serveur principal.
Pour effectuer ces modifications, remplacez le code commenté // YOUR STRIPE AND PAYPAL CODE WILL BE HERE
dans index.html.haml
par :
(function setupStripe() { //Initialize stripe with publishable key var stripe = Stripe("#{ENV['STRIPE_PUBLISHABLE_KEY']}"); //Create Stripe credit card elements. var elements = stripe.elements(); var card = elements.create('card'); //Add a listener in order to check if card.addEventListener('change', function(event) { //the div card-errors contains error details if any var displayError = document.getElementById('card-errors'); document.getElementById('submit-stripe').disabled = false; if (event.error) { // Display error displayError.textContent = event.error.message; } else { // Clear error displayError.textContent = ''; } }); // Mount Stripe card element in the #card-element div. card.mount('#card-element'); var form = document.getElementById('order-details'); // This will be called when the #submit-stripe button is clicked by the user. form.addEventListener('submit', function(event) { $('#submit-stripe').prop('disabled', true); event.preventDefault(); stripe.createToken(card).then(function(result) { if (result.error) { // Inform that there was an error. var errorElement = document.getElementById('card-errors'); errorElement.textContent = result.error.message; } else { // Now we submit the form. We also add a hidden input storing // the token. So our back-end can consume it. var $form = $("#order-details"); // Add a hidden input orders[token] $form.append($('<input type="hidden" name="orders[token]"/>').val(result.token.id)); // Set order type $('#order-type').val('stripe'); $form.submit(); } }); return false; }); }()); //YOUR PAYPAL CODE WILL BE HERE
Si vous visitez votre page, elle devrait ressembler à ceci avec les nouveaux champs de saisie sécurisés Stripe :

Étape 5 : Testez votre application.
Remplissez le formulaire de carte de crédit avec une carte de test (https://stripe.com/docs/testing) et soumettez la page. Vérifiez si l'action submit
est appelée avec tous les paramètres ( product_id , payment_gateway et token ) dans la sortie de votre serveur.
Frais de bande
Les frais Stripe représentent des transactions uniques. Par conséquent, après une transaction de frais Stripe, vous recevrez directement de l'argent du client. C'est idéal pour vendre des produits qui ne sont pas associés à des plans. Dans une section ultérieure, je montrerai comment effectuer le même type de transaction avec PayPal, mais le nom de PayPal pour ce type de transaction est Payment .
Dans cette section, je fournirai également tout le squelette pour gérer et soumettre une commande. Nous créons une commande dans l'action de submit
lorsque le formulaire Stripe est soumis. Cette commande aura initialement le statut en attente , donc si quelque chose ne va pas lors du traitement de cette commande, la commande sera toujours en attente .
Si une erreur survient lors des appels de l'API Stripe, nous définissons la commande dans un état d' échec , et si la charge est effectuée avec succès, elle sera dans l'état payé . L'utilisateur est également redirigé en fonction de la réponse de l'API Stripe comme indiqué dans le graphique suivant :

De plus, lorsqu'une charge Stripe est effectuée, un identifiant est renvoyé. Nous stockerons cet identifiant afin que vous puissiez le rechercher ultérieurement dans votre tableau de bord Stripe si nécessaire. Cet identifiant peut également être utilisé si la commande doit être remboursée. Une telle chose ne sera pas explorée dans cet article.
Étape 1 : Créez le service Stripe.
Nous utiliserons une classe singleton pour représenter les opérations Stripe à l'aide de l'API Stripe. Afin de créer une charge, la méthode Stripe::Charge.create
est appelée et l'attribut d'ID d'objet renvoyé sera stocké dans l'enregistrement de commande charge_id
. Cette fonction de create
est appelée en transmettant le jeton créé dans le frontal, le prix de la commande et une description.
Créez donc un nouveau dossier app/services/orders
, et ajoutez un service Stripe : app/services/orders/stripe.rb
contenant la classe singleton Orders::Stripe
, qui a une entrée dans la méthode execute
.
class Orders::Stripe INVALID_STRIPE_OPERATION = 'Invalid Stripe Operation' def self.execute(order:, user:) product = order.product # Check if the order is a plan if product.stripe_plan_name.blank? charge = self.execute_charge(price_cents: product.price_cents, description: product.name, card_token: order.token) else #SUBSCRIPTIONS WILL BE HANDLED HERE end unless charge&.id.blank? # If there is a charge with id, set order paid. order.charge_id = charge.id order.set_paid end rescue Stripe::StripeError => e # If a Stripe error is raised from the API, # set status failed and an error message order.error_message = INVALID_STRIPE_OPERATION order.set_failed end private def self.execute_charge(price_cents:, description:, card_token:) Stripe::Charge.create({ amount: price_cents.to_s, currency: "usd", description: description, source: card_token }) end end
Étape 2 : implémentez l'action de soumission et appelez le service Stripe.
Dans orders_controller.rb
, ajoutez ce qui suit dans l'action submit
, qui appellera essentiellement le service Orders::Stripe.execute
. Notez que deux nouvelles fonctions privées ont également été ajoutées : prepare_new_order
et order_params
.
def submit @order = nil #Check which type of order it is if order_params[:payment_gateway] == "stripe" prepare_new_order Orders::Stripe.execute(order: @order, user: current_user) elsif order_params[:payment_gateway] == "paypal" #PAYPAL WILL BE HANDLED HERE end ensure if @order&.save if @order.paid? # Success is rendered when order is paid and saved return render html: SUCCESS_MESSAGE elsif @order.failed? && [email protected]_message.blank? # Render error only if order failed and there is an error_message return render html: @order.error_message end end render html: FAILURE_MESSAGE end private # Initialize a new order and and set its user, product and price. def prepare_new_order @order = Order.new(order_params) @order.user_id = current_user.id @product = Product.find(@order.product_id) @order.price_cents = @product.price_cents end def order_params params.require(:orders).permit(:product_id, :token, :payment_gateway, :charge_id) end
Étape 3 : testez votre application.
Vérifiez si l'action submit, lorsqu'elle est appelée avec une carte de test valide, effectue une redirection vers un message réussi. De plus, vérifiez dans votre tableau de bord Stripe si la commande est également affichée.
Abonnements Stripe
Des abonnements ou des plans peuvent être créés pour des paiements récurrents. Avec ce type de produit, l'utilisateur est automatiquement facturé quotidiennement, hebdomadairement, mensuellement ou annuellement selon la configuration du plan. Dans cette section, nous utiliserons le champ pour le produit stripe_plan_name
afin de stocker l'ID du plan - en fait, il nous est possible de choisir l'ID, et nous l'appellerons premium-plan
- qui sera utilisé pour créer le relation customer <-> subscription
.
Nous allons également créer une nouvelle colonne pour la table des utilisateurs appelée stripe_customer_id
qui sera remplie avec la propriété id d'un objet client Stripe. Un client Stripe est créé lorsque la fonction Stripe::Customer.create
est appelée, et vous pouvez également vérifier les clients créés et liés à votre compte dans (https://dashboard.stripe.com/test/customers). Les clients sont créés en passant un paramètre source
qui, dans notre cas, est le jeton généré dans le frontal qui est envoyé lors de la soumission du formulaire.
L'objet client obtenu à partir du dernier appel d'API Stripe mentionné est également utilisé pour créer un abonnement, ce qui se fait en appelant customer.subscriptions.create
et en transmettant l'ID du plan en tant que paramètre.
De plus, la gemme stripe-rails
fournit l'interface pour récupérer et mettre à jour un client à partir de Stripe, ce qui se fait en appelant Stripe::Customer.retrieve
et Stripe::Customer.update
, respectivement.
Ainsi, lorsqu'un enregistrement d'utilisateur a déjà un stripe_customer_id
, au lieu de créer un nouveau client en utilisant Stripe::Customer.create
, nous appellerons Stripe::Customer.retrieve
en passant le stripe_customer_id
comme paramètre, suivi d'un Stripe::Customer.update
, et dans ce cas, en passant le jeton en paramètre.

Tout d'abord, nous allons créer un plan à l'aide de l'API Stripe afin de pouvoir créer un nouveau produit d'abonnement en utilisant le champ stripe_plan_name
. Ensuite, nous apporterons des modifications dans le service orders_controller
et Stripe afin que la création et l'exécution des abonnements Stripe soient gérées.
Étape 1 : Créez un plan à l'aide de l'API Stripe.
Ouvrez votre console en utilisant les rails c
. Créez un abonnement pour votre compte Stripe avec :
Stripe::Plan.create({ amount: 10000, interval: 'month', product: { name: 'Premium plan', }, currency: 'usd', id: 'premium-plan', })
Si le résultat renvoyé à cette étape est vrai, cela signifie que le plan a été créé avec succès et que vous pouvez y accéder dans votre tableau de bord Stripe.
Étape 2 : Créez un produit dans la base de données avec le champ stripe_plan_name
défini.
Maintenant, créez un produit avec le stripe_plan_name
défini comme premium-plan
dans la base de données :
Product.create(price_cents: 10000, name: 'Premium Plan', stripe_plan_name: 'premium-plan')
Étape 3 : Générez une migration pour ajouter une colonne stripe_customer_id
dans la table des users
.
Exécutez ce qui suit dans le terminal :
rails generate migration AddStripeCustomerIdToUser stripe_customer_id:string rails db:migrate
Étape 4 : implémentez la logique d'abonnement dans la classe de service Stripe.
Ajoutez deux fonctions supplémentaires dans les méthodes privées de app/services/orders/stripe.rb
: execute_subscription
est responsable de la création des abonnements dans l'objet du client. La fonction find_or_create_customer
est chargée de retourner le client déjà créé ou en retournant un client nouvellement créé.
def self.execute_subscription(plan:, token:, customer:) customer.subscriptions.create({ plan: plan }) end def self.find_or_create_customer(card_token:, customer_id:, email:) if customer_id stripe_customer = Stripe::Customer.retrieve({ id: customer_id }) if stripe_customer stripe_customer = Stripe::Customer.update(stripe_customer.id, { source: card_token}) end else stripe_customer = Stripe::Customer.create({ email: email, source: card_token }) end stripe_customer end
Enfin, dans la fonction execute
du même fichier ( app/services/orders/stripe.rb
), nous allons d'abord appeler find_or_create_customer
puis exécuter l'abonnement en appelant execute_subscription
en passant le précédent client récupéré/créé. Remplacez donc le commentaire #SUBSCRIPTIONS WILL BE HANDLED HERE
dans la méthode d' execute
par le code suivant :
customer = self.find_or_create_customer(card_token: order.token, customer_id: user.stripe_customer_id, email: user.email) if customer user.update(stripe_customer_id: customer.id) order.customer_id = customer.id charge = self.execute_subscription(plan: product.stripe_plan_name, customer: customer)
Étape 5 : Testez votre application.
Visitez votre site Web, sélectionnez le produit d'abonnement Premium Plan
et remplissez une carte de test valide. Après la soumission, il devrait vous rediriger vers une page réussie. De plus, vérifiez dans votre tableau de bord Stripe si l'abonnement a été créé avec succès.
Configurer PayPal
Comme nous l'avons fait dans Stripe, nous ajouterons également un joyau pour l'utilisation de l'API PayPal : paypal-sdk-rest
, et la création d'un compte PayPal est également requise. Un workflow descriptif pour PayPal utilisant cette gemme peut être consulté dans la documentation officielle de l'API PayPal.
Étape 1 : Ajoutez la gemme paypal-sdk-rest
à votre projet.
Ajoutez ceci dans le Gemfile
:
gem 'paypal-sdk-rest'
Cours:
bundle install
Étape 2 : Générez vos clés API.
Afin d'avoir les clés API pour communiquer avec PayPal, vous devrez créer un compte PayPal. Alors:
- Créez un compte (ou utilisez votre compte PayPal) sur https://developer.paypal.com/.
- Toujours connecté à votre compte, créez deux comptes sandbox sur https://developer.paypal.com/developer/accounts/ :
- Personnel (compte acheteur) – Il sera utilisé dans vos tests pour effectuer des paiements et des abonnements.
- Entreprise (compte marchand) - Ceci sera lié à l'application, qui aura les clés API que nous recherchons. De plus, toutes les transactions peuvent être suivies dans ce compte.
- Créez une application sur https://developer.paypal.com/developer/applications en utilisant l'ancien compte sandbox professionnel.
- Après cette étape, vous recevrez deux clés pour PayPal :
Client ID
etSecret
. - Dans
config/application.yml
, remplacezYOUR_CREDENTIAL_HERE
dePAYPAL_CLIENT_ID
etPAYPAL_CLIENT_SECRET
par les clés que vous venez de recevoir.
Étape 3 : Initialisez le module PayPal.
Semblable à Stripe, en plus de remplacer les clés dans application.yml
, nous devons encore initialiser le module PayPal afin qu'il puisse utiliser les clés déjà définies dans notre variable ENV
. Pour cela, créez un fichier dans config/initializers/paypal.rb
avec :
PayPal::SDK.configure( mode: ENV['PAYPAL_ENV'], client_id: ENV['PAYPAL_CLIENT_ID'], client_secret: ENV['PAYPAL_CLIENT_SECRET'], ) PayPal::SDK.logger.level = Logger::INFO
Étape 4 : Intégrez PayPal dans le front-end.
Dans index.html.haml
, ajoutez ceci en haut du fichier :
%script(src="https://www.paypal.com/sdk/js?client-id=#{ENV['PAYPAL_CLIENT_ID']}")
Contrairement à Stripe, PayPal utilise uniquement un bouton qui, lorsqu'il est cliqué, ouvre une fenêtre contextuelle sécurisée où l'utilisateur peut se connecter et procéder au paiement/à l'abonnement. Ce bouton peut être rendu en appelant la méthode paypal.Button(PARAM1).render(PARAM2)
.
-
PARAM1
est un objet avec la configuration de l'environnement et deux fonctions de rappel comme propriétés :createOrder
etonApprove
. -
PARAM2
indique l'identifiant de l'élément HTML auquel le bouton PayPal doit être attaché.
Ainsi, toujours dans le même fichier, remplacez le code commenté YOUR PAYPAL CODE WILL BE HERE
par :
(function setupPaypal() { function isPayment() { return $('[data-charges-and-payments-section] input[name="orders[product_id]"]:checked').length } function submitOrderPaypal(chargeID) { var $form = $("#order-details"); // Add a hidden input orders[charge_id] $form.append($('<input type="hidden" name="orders[charge_id]"/>').val(chargeID)); // Set order type $('#order-type').val('paypal'); $form.submit(); } paypal.Buttons({ env: "#{ENV['PAYPAL_ENV']}", createOrder: function() { }, onApprove: function(data) { } }).render('#submit-paypal'); }());
Étape 5 : Testez votre application.
Visitez votre page et vérifiez si le bouton PayPal s'affiche lorsque vous sélectionnez PayPal comme mode de paiement.
Opérations PayPal
La logique des transactions PayPal, contrairement à Stripe, est un peu plus complexe en impliquant davantage de demandes provenant du front-end vers le back-end. C'est pourquoi cette section existe. J'expliquerai plus ou moins (sans aucun code) comment les fonctions décrites dans les méthodes createOrder
et onApprove
vont être implémentées et ce qui est également attendu dans les processus back-end.
Étape 1 : Lorsque l'utilisateur clique sur le bouton d'envoi de PayPal, une fenêtre contextuelle PayPal demandant les informations d'identification de l'utilisateur s'ouvre mais est en cours de chargement. La fonction callback createOrder
est appelée.

Étape 2 : Dans cette fonction, nous effectuerons une demande à notre back-end qui créera un paiement/abonnement. C'est le tout début d'une transaction, et aucun frais ne sera appliqué pour le moment, donc la transaction est en fait dans un état en attente . Notre serveur principal devrait nous renvoyer un jeton, qui sera généré à l'aide du module PayPal (fourni via le gem paypal-rest-sdk
).
Étape 3 : Toujours dans le rappel createOrder
, nous renvoyons ce jeton généré dans notre back-end, et si tout va bien, la fenêtre contextuelle PayPal affichera ce qui suit, demandant les informations d'identification de l'utilisateur :

Étape 4 : Une fois que l'utilisateur s'est connecté et a sélectionné le mode de paiement, la fenêtre contextuelle changera son état comme suit :

Étape 5 : Le rappel de la fonction onApprove
est maintenant appelé. Nous l'avons défini comme suit : onApprove: function(data)
. L'objet data
aura les informations de paiement afin de l'exécuter. Dans ce rappel, une autre demande à notre fonction back-end sera effectuée cette fois en passant l'objet de données afin d'exécuter la commande PayPal.
Étape 6 : Notre serveur principal exécute cette transaction et renvoie 200 (en cas de succès).
Étape 7 : Lorsque notre back-end revient, nous soumettons le formulaire. Il s'agit de la troisième requête que nous adressons à notre back-end.
Notez que, contrairement à Stripe, trois requêtes sont adressées à notre back-end dans ce processus. Et nous garderons l'état de notre dossier de commande synchronisé en conséquence :
-
createOrder
: une transaction est créée et un enregistrement de commande est également créé ; par conséquent, il est dans un état en attente par défaut. -
onApprove
: la transaction est exécutée et notre commande sera définie comme paypal_executed . - La page de commande est soumise : La transaction a déjà été exécutée, donc rien ne change. L'enregistrement de la commande changera son état en payé .
Tout ce processus est décrit dans le graphique suivant :

Paiements PayPal
Les paiements PayPal suivent la même logique que Stripe Charges, ils représentent donc des transactions uniques, mais comme mentionné dans la section précédente, ils ont une logique de flux différente. Voici les changements qui devront être effectués pour gérer les paiements PayPal :
Étape 1 : Créez de nouveaux itinéraires pour PayPal et exécutez les paiements.
Add the following routes in config/routes.rb
:
post 'orders/paypal/create_payment' => 'orders#paypal_create_payment', as: :paypal_create_payment post 'orders/paypal/execute_payment' => 'orders#paypal_execute_payment', as: :paypal_execute_payment
This will create two new routes for creating and executing payments which will be handled in the paypal_create_payment
and paypal_execute_payment
orders controller methods.
Step 2: Create the PayPal service.
Add the singleton class Orders::Paypal
at: app/services/orders/paypal.rb
.
This service will initially have three responsibilities:
- The
create_payment
method creates a payment by callingPayPal::SDK::REST::Payment.new
. A token is generated and returned to the front-end. - The
execute_payment
method executes the payment by first finding the previous created payment object throughPayPal::SDK::REST::Payment.find(payment_id)
which uses the payment_id as an argument which has the same value as the charge_id stored in the previous step in the order object. After that, we callexecute
in the payment object with a given payer as the parameter. This payer is given by the front end after the user has provided credentials and selected a payment method in the popup. - The
finish
method finds an order by a specific charge_id querying for recently created orders in the paypal_executed state. If a record is found, it is marked as paid.
class Orders::Paypal def self.finish(charge_id) order = Order.paypal_executed.recently_created.find_by(charge_id: charge_id) return nil if order.nil? order.set_paid order end def self.create_payment(order:, product:) payment_price = (product.price_cents/100.0).to_s currency = "USD" payment = PayPal::SDK::REST::Payment.new({ intent: "sale", payer: { payment_method: "paypal" }, redirect_urls: { return_url: "/", cancel_url: "/" }, transactions: [{ item_list: { items: [{ name: product.name, sku: product.name, price: payment_price, currency: currency, quantity: 1 } ] }, amount: { total: payment_price, currency: currency }, description: "Payment for: #{product.name}" }] }) if payment.create order.token = payment.token order.charge_id = payment.id return payment.token if order.save end end def self.execute_payment(payment_id:, payer_id:) order = Order.recently_created.find_by(charge_id: payment_id) return false unless order payment = PayPal::SDK::REST::Payment.find(payment_id) if payment.execute( payer_id: payer_id ) order.set_paypal_executed return order.save end end
Step 3: Call the PayPal service in the controller in the submit action.
Add a callback for prepare_new_order
before the action paypal_create_payment
(which will be added in the next step) is requested by adding the following in the file app/controllers/orders_controller.rb
:
class OrdersController < ApplicationController before_action :authenticate_user! before_action :prepare_new_order, only: [:paypal_create_payment] ...
Again, in the same file, call PayPal service in the submit action by replacing the commented code #PAYPAL WILL BE HANDLED HERE.
avec ce qui suit :
... elsif order_params[:payment_gateway] == "paypal" @order = Orders::Paypal.finish(order_params[:token]) end ...
Step 4: Create the actions for handling requests.
Still, in the app/controllers/orders_controller.rb
file, create two new actions (which should be public) for handling requests to paypal_create_payment
and paypal_execute_payment
routes:
- The
paypal_create_payment
method: Will call our service methodcreate_payment
. If that returns successfully, it will return the order token created byOrders::Paypal.create_payment
. - The
paypal_execute_payment
method: Will call our service methodexecute_payment
(which executes our payments). If the payment is performed successfully, it returns 200.
... def paypal_create_payment result = Orders::Paypal.create_payment(order: @order, product: @product) if result render json: { token: result }, status: :ok else render json: {error: FAILURE_MESSAGE}, status: :unprocessable_entity end end def paypal_execute_payment if Orders::Paypal.execute_payment(payment_id: params[:paymentID], payer_id: params[:payerID]) render json: {}, status: :ok else render json: {error: FAILURE_MESSAGE}, status: :unprocessable_entity end end ...
Step 5: Implement the front-end callback functions for createOrder
and onApprove
.
Make your paypal.Button.render
call look like this:
paypal.Buttons({ env: "#{ENV['PAYPAL_ENV']}", createOrder: function() { $('#order-type').val("paypal"); if (isPayment()) { return $.post("#{paypal_create_payment_url}", $('#order-details').serialize()).then(function(data) { return data.token; }); } else { } }, onApprove: function(data) { if (isPayment()) { return $.post("#{paypal_execute_payment_url}", { paymentID: data.paymentID, payerID: data.payerID }).then(function() { submitOrderPaypal(data.paymentID) }); } else { } } }).render('#submit-paypal');
As mentioned in the previous section, we call paypal_create_payment_url
for the createOrder
callback and paypal_execute_payment_url
for the onApprove
callback. Notice that if the last request returns success, we submit the order, which is the third request made to the server.
In the createOrder
function handler, we return a token (obtained from the back end). In the onApprove
callback, we have two properties passed down to our back-end paymentID
and payerID
. These will be used in order to execute the payment.
Finally, notice that we have two empty else
clauses as I'm leaving room for the next section where we will be adding PayPal subscriptions.
If you visit your page after integrating the front-end JavaScript section and select PayPal as the payment method, it should look like the following:

Step 6: Test your application.
- Visitez la page d'accueil.
- Sélectionnez un produit de paiement/charge et PayPal comme mode de paiement.
- Cliquez sur le bouton Soumettre PayPal.
- Dans la fenêtre contextuelle PayPal :
- Utilisez les identifiants du compte acheteur que vous avez créé.
- Connectez-vous et confirmez votre commande.
- La popup devrait se fermer.
- Vérifiez si vous êtes redirigé vers une page de réussite.
- Enfin, vérifiez si la commande a été effectuée dans le compte PayPal en vous connectant avec votre compte professionnel sur https://www.sandbox.paypal.com/signin et en consultant le tableau de bord https://www.sandbox.paypal.com/listing /transactions.
Abonnements PayPal
Les plans/accords/abonnements PayPal suivent la même logique que les abonnements Stripe et sont créés pour les paiements récurrents. Avec ce type de produit, l'utilisateur est automatiquement facturé quotidiennement, hebdomadairement, mensuellement ou annuellement selon sa configuration.
Nous utiliserons le champ pour le produit paypal_plan_name
, afin de stocker l'ID de plan fourni par PayPal. Dans ce cas, contrairement à Stripe, on ne choisit pas l'ID, et PayPal retourne cette valeur qui servira à mettre à jour le dernier produit créé dans notre base de données.
Pour créer un abonnement, aucune information customer
n'est requise à aucune étape, car la méthode onApprove
gère probablement ce lien dans son implémentation sous-jacente. Nos tables resteront donc les mêmes.
Étape 1 : Créez un forfait à l'aide de l'API PayPal.
Ouvrez votre console en utilisant les rails c
. Créez un abonnement pour votre compte PayPal avec :
plan = PayPal::SDK::REST::Plan.new({ name: 'Premium Plan', description: 'Premium Plan', type: 'fixed', payment_definitions: [{ name: 'Premium Plan', type: 'REGULAR', frequency_interval: '1', frequency: 'MONTH', cycles: '12', amount: { currency: 'USD', value: '100.00' } }], merchant_preferences: { cancel_url: 'http://localhost:3000/', return_url: 'http://localhost:3000/', max_fail_attempts: '0', auto_bill_amount: 'YES', initial_fail_amount_action: 'CONTINUE' } }) plan.create plan_update = { op: 'replace', path: '/', value: { state: 'ACTIVE' } } plan.update(plan_update)
Étape 2 : Mettez à jour le dernier produit dans la base de données paypal_plan_name
avec le plan.id
renvoyé.
Cours:
Product.last.update(paypal_plan_name: plan.id)
Étape 3 : Ajoutez des itinéraires pour l'abonnement PayPal.
Ajoutez deux nouvelles routes dans config/routes.rb
:
post 'orders/paypal/create_subscription' => 'orders#paypal_create_subscription', as: :paypal_create_subscription post 'orders/paypal/execute_subscription' => 'orders#paypal_execute_subscription', as: :paypal_execute_subscription
Étape 4 : Gérez la création et l'exécution dans le service PayPal.
Ajoutez deux fonctions supplémentaires pour créer et exécuter des abonnements dans Orders::Paypal
of app/services/orders/paypal.rb
:
def self.create_subscription(order:, product:) agreement = PayPal::SDK::REST::Agreement.new({ name: product.name, description: "Subscription for: #{product.name}", start_date: (Time.now.utc + 1.minute).iso8601, payer: { payment_method: "paypal" }, plan: { id: product.paypal_plan_name } }) if agreement.create order.token = agreement.token return agreement.token if order.save end end def self.execute_subscription(token:) order = Order.recently_created.find_by(token: token) return false unless order agreement = PayPal::SDK::REST::Agreement.new agreement.token = token if agreement.execute order.charge_id = agreement.id order.set_paypal_executed return order.charge_id if order.save end end
Dans create_subscription
, nous initialisons un accord en appelant la méthode PayPal::SDK::REST::Agreement.new
et en transmettant le product.paypal_plan_name
comme l'un de ses attributs. Ensuite, nous le créons, et maintenant un jeton sera défini pour ce dernier objet. Nous renvoyons également le jeton au frontal.
Dans execute_subscription
, nous retrouvons l'enregistrement de order
créé lors de l'appel précédent. Après cela, nous initialisons un nouvel accord, nous définissons le jeton de cet objet précédent et l'exécutons. Si cette dernière étape est effectuée avec succès, le statut de la commande est défini sur paypal_executed . Et maintenant, nous retournons au front-end l'identifiant de l'accord qui est également stocké dans order.chager_id
.
Étape 5 : Ajoutez des actions pour créer et exécuter des abonnements dans orders_controller
.
Modifiez le app/controllers/orders_controller.rb
. En haut de la classe, d'abord, puis mettez à jour le rappel prepare_new_order
pour qu'il soit également exécuté avant que paypal_create_subscription
ne soit appelé :
class OrdersController < ApplicationController before_action :authenticate_user! before_action :prepare_new_order, only: [:paypal_create_payment, :paypal_create_subscription]
De plus, dans le même fichier, ajoutez les deux fonctions publiques afin qu'elles appellent le service Orders::Paypal
avec un flux similaire à celui que nous avons déjà dans les paiements PayPal :
... def paypal_create_subscription result = Orders::Paypal.create_subscription(order: @order, product: @product) if result render json: { token: result }, status: :ok else render json: {error: FAILURE_MESSAGE}, status: :unprocessable_entity end end def paypal_execute_subscription result = Orders::Paypal.execute_subscription(token: params[:subscriptionToken]) if result render json: { id: result}, status: :ok else render json: {error: FAILURE_MESSAGE}, status: :unprocessable_entity end end ...
Étape 6 : Ajout de gestionnaires d'abonnement pour createOrder
et onApprove
dans le frontal.
Enfin, dans index.html.haml
, remplacez la fonction paypal.Buttons
par la suivante, qui remplira les deux else
vides que nous avions auparavant :
paypal.Buttons({ env: "#{ENV['PAYPAL_ENV']}", createOrder: function() { $('#order-type').val("paypal"); if (isPayment()) { return $.post("#{paypal_create_payment_url}", $('#order-details').serialize()).then(function(data) { return data.token; }); } else { return $.post("#{paypal_create_subscription_url}", $('#order-details').serialize()).then(function(data) { return data.token; }); } }, onApprove: function(data) { if (isPayment()) { return $.post("#{paypal_execute_payment_url}", { paymentID: data.paymentID, payerID: data.payerID }).then(function() { submitOrderPaypal(data.paymentID) }); } else { return $.post("#{paypal_execute_subscription_url}", { subscriptionToken: data.orderID }).then(function(executeData) { submitOrderPaypal(executeData.id) }); } } }).render('#submit-paypal');
La création et l'exécution des abonnements ont une logique similaire à celle utilisée pour les paiements. Une différence est que lors de l'exécution des paiements, les données de la fonction de rappel onApprove
ont déjà un paymentID
représentant le charge_id
pour soumettre le formulaire via submitOrderPaypal(data.paymentID)
. Pour les abonnements, nous obtenons le charge_id
uniquement après l'avoir exécuté en demandant un POST
sur paypal_execute_subscription_url
, nous pouvons donc appeler submitOrderPaypal(executeData.id)
.
Étape 7 : testez votre application.
- Visitez la page d'accueil.
- Sélectionnez un produit d'abonnement et PayPal comme mode de paiement.
- Cliquez sur le bouton Soumettre PayPal.
- Dans la fenêtre contextuelle PayPal :
- Utilisez les identifiants du compte acheteur que vous avez créé.
- Connectez-vous et confirmez votre commande.
- La popup devrait se fermer.
- Vérifiez si vous êtes redirigé vers une page de réussite.
- Enfin, vérifiez si la commande a été effectuée sur le compte PayPal en vous connectant avec votre compte professionnel sur https://www.sandbox.paypal.com/signin et en consultant le tableau de bord https://www.sandbox.paypal.com/listing/ transactions.
Conclusion
Après avoir lu cet article, vous devriez être en mesure d'intégrer les paiements/charges ainsi que les transactions d'abonnements pour PayPal et Stripe dans votre application Rails. Il y a beaucoup de points qui pourraient être améliorés que je n'ai pas ajoutés dans cet article par souci de brièveté. J'ai tout organisé en partant d'une hypothèse de difficulté :
- Plus facile:
- Utilisez Transport Layer Security (TLS) pour que vos requêtes utilisent HTTPS.
- Implémentez des configurations d'environnement de production pour PayPal et Stripe.
- Ajoutez une nouvelle page afin que les utilisateurs puissent accéder à un historique des commandes précédentes.
- Moyen:
- Rembourser ou annuler des abonnements.
- Fournir une solution pour les paiements des utilisateurs non enregistrés.
- Plus fort:
- Fournissez un moyen de supprimer des comptes et de conserver leur jeton et leur identifiant client si l'utilisateur souhaite revenir. Mais après un certain nombre de jours, supprimez ces données afin que votre application soit plus conforme à la norme PCI.
- Passer à l'API PayPal version 2 côté serveur (https://developer.paypal.com/docs/api/payments/v2/) La gemme que nous avons utilisée dans ce tutoriel paypal-sdk-rest , n'a qu'une version bêta pour la version 2, donc cela pourrait être utilisé avec prudence (https://github.com/paypal/PayPal-Ruby-SDK/tree/2.0-beta).
- Inclure les requêtes idempotentes.
- Bande : https://stripe.com/docs/api/idempotent_requests
- PayPal : https://developer.paypal.com/docs/api-basics/#api-idempotency
Je recommande également de lire sur l'élément Stripe Checkout, qui est une autre façon d'intégrer Stripe dans le front-end. Contrairement à Stripe Elements, que nous avons utilisé dans ce tutoriel, Stripe Checkout ouvre une fenêtre contextuelle après avoir cliqué sur un bouton (similaire à PayPal) où l'utilisateur remplit les informations de carte de crédit OU choisit de payer avec Google Pay/Apple Pay https://stripe.com /docs/web.
Une deuxième recommandation de lecture concerne les pages de sécurité pour les deux passerelles de paiement.
- Pour Bande
- Pour Paypal
Enfin, merci d'avoir lu cet article ! Vous pouvez également consulter mon projet GitHub utilisé pour cet exemple de projet. Là, j'ai également ajouté des tests rspec lors du développement.