Integrazione dei metodi di pagamento Stripe e PayPal in Ruby on Rails

Pubblicato: 2022-03-11

Una caratteristica fondamentale per le grandi aziende di eCommerce come AliExpress, Ebay e Amazon è un modo sicuro di gestire i pagamenti, essenziale per la loro attività. Se questa funzione fallisce, le conseguenze sarebbero devastanti. Questo vale per i leader del settore e gli sviluppatori Ruby on Rails che lavorano su app di eCommerce.

La sicurezza informatica è essenziale per prevenire gli attacchi e un modo per rendere più sicuro il processo di transazione è chiedere a un servizio di terze parti di gestirlo. Includere i gateway di pagamento nella tua applicazione è un modo per raggiungere questo obiettivo, in quanto forniscono l'autorizzazione dell'utente, la crittografia dei dati e un dashboard in modo da poter seguire lo stato della transazione al volo.

Esistono numerosi servizi di gateway di pagamento sul Web, ma in questo articolo mi concentrerò sull'integrazione di Stripe e PayPal in un'applicazione Rails. Per citarne alcuni altri: Amazon Payments, Square, SecurePay, WorldPay, Authorize.Net, 2Checkout.com, Braintree, Amazon o BlueSnap.

Come funziona l'integrazione del gateway di pagamento

Rappresentanza generale per le transazioni che coinvolgono gateway di pagamento
Rappresentanza generale per le transazioni che coinvolgono gateway di pagamento

In generale, nella tua applicazione sarà presente un modulo/pulsante in cui l'utente può accedere/inserire i dati della carta di credito. PayPal e Stripe rendono già più sicuro questo primo passaggio utilizzando moduli iframe o popups che impediscono alla tua applicazione di memorizzare informazioni sensibili sulla carta di credito dell'utente poiché restituiranno un token che rappresenta questa transazione. Alcuni utenti potrebbero anche sentirsi più sicuri di elaborare i pagamenti sapendo che un servizio di terze parti sta gestendo il processo di transazione, quindi questo può anche essere un'attrazione per la tua applicazione.

Dopo aver autenticato le informazioni dell'utente, un gateway di pagamento confermerà il pagamento contattando un elaboratore di pagamento che comunica con le banche per regolare i pagamenti. Ciò garantisce che la transazione venga addebitata/accreditata correttamente.

Stripe utilizza un modulo di carta di credito chiedendo il numero della carta di credito, cvv e data di scadenza. Quindi l'utente deve compilare i dati della carta di credito negli input protetti di Stripe. Dopo aver fornito queste informazioni, il back-end dell'applicazione elabora questo pagamento tramite un token.

A differenza di Stripe, PayPal reindirizza l'utente alla pagina di accesso di PayPal. L'utente autorizza e seleziona il metodo di pagamento tramite PayPal e, ancora una volta, il tuo back-end gestirà i token anziché i dati sensibili dell'utente.

È importante ricordare che, per questi due gateway di pagamento, il tuo back-end dovrebbe chiedere di procedere all'esecuzione della transazione tramite le API Stripe o PayPal che daranno una risposta OK/NOK, quindi la tua applicazione dovrebbe reindirizzare l'utente a una pagina di successo o di errore di conseguenza.

L'intento di questo articolo è fornire una guida rapida per l'integrazione di questi due gateway di pagamento in un'unica applicazione. Per tutti i test, utilizzeremo sandbox e account di prova forniti da Stripe e PayPal per simulare i pagamenti.

Impostare

Prima di integrare i gateway di pagamento, faremo una configurazione per inizializzare l'applicazione aggiungendo gemme, tabelle di database e una pagina di indice. Questo progetto è stato creato utilizzando Rails versione 5.2.3 e Ruby 2.6.3.

Nota: puoi controllare le nuove funzionalità di Rails 6 nel nostro recente articolo.

Passaggio 1: inizializza un'applicazione Rails.

Inizializza il progetto eseguendo l'inizializzazione del progetto con il comando rails con il nome della tua app:

 rails new YOUR_APP_NAME

E cd nella cartella dell'applicazione.

Passaggio 2: installa gemme.

Oltre alle gemme Stripe e PayPal, sono state aggiunte alcune altre gemme:

  • devise : utilizzato per l'autenticazione e l'autorizzazione dell'utente
  • haml : strumento di creazione di modelli per il rendering di pagine utente
  • jquery-rails : per jquery negli script front-end
  • money-rails : per visualizzare valori monetari formattati

Aggiungi al tuo Gemfile :

 gem "devise", ">= 4.7.1" gem "haml" gem "jquery-rails" gem "money-rails"

Dopo averlo aggiunto, esegui nella tua CLI:

 bundle install

Passaggio 3: inizializza le gemme.

Alcune di queste gemme richiederanno l'inizializzazione oltre all'installazione tramite bundle .

Dispositivo di installazione:

 rails g devise:install

Inizializzazione money-rails :

 rails g money_rails:initializer

Inizializza jquery-rails aggiungendo in fondo a app/assets/javascripts/application.js quanto segue:

 //= require jquery //= require jquery_ujs

Passaggio 4: tabelle e migrazioni

In questo progetto verranno utilizzate tre tabelle Utenti , Prodotti e Ordini .

  • Users : verranno generati tramite dispositivo
  • Colonne dei Products :
    • name
    • price_cents
    • Stripe_plan_name : un ID che rappresenta un piano di abbonamento creato in Stripe, in modo che gli utenti possano abbonarsi. Questo campo è obbligatorio solo per i prodotti associati a un piano Stripe.
    • paypal_plan_name : Lo stesso di stripe_plan_name ma per PayPal
  • Colonne Orders :
    • product_id
    • user_id
    • status : Questo informerà se l'ordine è in sospeso, non riuscito o pagato.
    • token : Questo è un token generato dalle API (Stripe o PayPal) per inizializzare una transazione.
    • price_cents : simile al prodotto, ma utilizzato per rendere persistente questo valore nel record dell'ordine
    • payment_gateway : memorizza il gateway di pagamento utilizzato per l'ordine PayPal o Stripe
    • customer_id : verrà utilizzato per Stripe per memorizzare il cliente Stripe per un abbonamento e verrà spiegato con maggiori dettagli in una sezione successiva.

Per generare queste tabelle, è necessario generare alcune migrazioni:

Per creare la tabella Utenti . Correre:

 rails g devise User

Per creare la tabella Prodotti . Genera una migrazione eseguendo:

 rails generate migration CreateProducts name:string stripe_plan_name:string paypal_plan_name:string

Apri il file di migrazione creato, che dovrebbe trovarsi in db/migrate/ , e apporta le modifiche per rendere la migrazione simile a questa:

 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

Per creare la tabella Ordini . Genera una migrazione eseguendo:

 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

Ancora una volta, apri il file di migrazione creato che dovrebbe trovarsi in db/migrate/ e apporta le modifiche a quel file per farlo sembrare simile a questo:

 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

Esegui le migrazioni del database eseguendo:

 rails db:migrate

Passaggio 5: crea modelli.

Il modello utente è già stato creato dall'installazione del dispositivo e non sarà richiesta alcuna modifica. Oltre a ciò, verranno creati due modelli per Prodotto e Ordine .

Prodotto. Aggiungi un nuovo file, app/models/product.rb , con:

 class Product < ActiveRecord::Base monetize :price_cents has_many :orders end

Ordine. Aggiungi un nuovo file, app/models/order.rb , con:

 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

Passaggio 6: popolare il database.

Nella console verranno creati un utente e due prodotti. I record degli ordini verranno creati in base ai test di pagamento.

  • rails s
  • Nel tuo browser, visita http://localhost:3000
  • Verrai reindirizzato a una pagina di registrazione.
  • Registra un utente inserendo il suo indirizzo email e la sua password.
  • Nel tuo terminale, verranno richiesti i seguenti log che mostrano che un utente è stato creato nel tuo database:
 User Create (0.1ms) INSERT INTO "users" ("email", "encrypted_password", "created_at", "updated_at") VALUES (?, ?, ?, ?) …
  • Crea due prodotti senza abbonamenti eseguendo i rails c e aggiungendo:
    • Product.create(name: "Awesome T-Shirt", price_cents: 3000)
    • Product.create(name: "Awesome Sneakers", price_cents: 5000)

Passaggio 7: crea una pagina indice

La pagina principale del progetto include la selezione dei prodotti per gli acquisti o gli abbonamenti. Inoltre, ha anche una sezione per la selezione del metodo di pagamento (Stripe o PayPal). Un pulsante di invio viene utilizzato anche per ogni tipo di gateway di pagamento poiché per PayPal aggiungeremo il proprio design del pulsante attraverso la sua libreria JavaScript.

Innanzitutto, crea le rotte per index e submit in config/routes.rb .

 Rails.application.routes.draw do devise_for :users get '/', to: 'orders#index' post '/orders/submit', to: 'orders#submit' end

Crea e aggiungi azioni index e submit nel controller degli ordini app/controllers/orders_controller.rb . L'azione orders#index memorizza due variabili da consumare nel front-end: @products_purchase che ha un elenco di prodotti senza piani e @products_subscription che ha prodotti con entrambi i piani PayPal e 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

Crea un file in app/views/orders/index.html.haml . Questo file contiene tutti gli input che invieremo al nostro back-end tramite il metodo di invio e l'interazione per i gateway di pagamento e la selezione dei prodotti. Di seguito sono riportati alcuni attributi del nome di input:

  • Orders[product_id] memorizza l'id del prodotto.
  • Orders[payment_gateway] contiene il gateway di pagamento con i valori Stripe o PayPal per l'altro.
 %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; }

Se esegui la tua applicazione con rails s se visita la tua pagina in http://localhost:3000 . Dovresti essere in grado di vedere la pagina come segue:

Pagina indice grezza senza integrazione Stripe e PayPal
Pagina indice grezza senza integrazione Stripe e PayPal

Archiviazione delle credenziali del gateway di pagamento

Le chiavi PayPal e Stripe verranno archiviate in un file non tracciato da Git. Ci sono due tipi di chiavi memorizzate in questo file per ogni gateway di pagamento e per ora utilizzeremo un valore fittizio per loro. Ulteriori istruzioni per la creazione di queste chiavi sono presentate in ulteriori sezioni.

Passaggio 1: aggiungi questo in .gitignore .

 /config/application.yml

Passaggio 2: crea un file con le tue credenziali in config/application.yml . Dovrebbe contenere tutte le tue chiavi sandbox/test PayPal e Stripe per accedere a queste 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

Passaggio 3: per archiviare le variabili dal file config/application.yml all'avvio dell'applicazione, aggiungere queste righe in config/application.rb all'interno della classe Application in modo che siano disponibili in ENV .

 config_file = Rails.application.config_for(:application) config_file.each do |key,value| ENV[key] = value end unless config_file.nil?

Configurazione a strisce

Aggiungeremo una gemma per l'utilizzo dell'API Stripe: stripe-rails . È inoltre necessaria la creazione di un account Stripe in modo che gli addebiti e gli abbonamenti possano essere elaborati. Se necessario, puoi consultare i metodi API per l'API Stripe nella documentazione ufficiale.

Passaggio 1: aggiungi la gemma stripe-rails al tuo progetto.

La gemma stripe-rails fornirà un'interfaccia per tutte le richieste API utilizzate in questo progetto.

Aggiungi questo nel Gemfile :

 gem 'stripe-rails'

Correre:

 bundle install

Passaggio 2: genera le chiavi API.

Per avere le chiavi API per comunicare con Stripe, dovrai creare un account in Stripe. Per testare l'applicazione, è possibile utilizzare la modalità di test, quindi non è necessario inserire informazioni aziendali reali nel processo di creazione dell'account Stripe.

  • Crea un account in Stripe se non ne hai uno (https://dashboard.stripe.com/).
  • Mentre sei ancora nella dashboard di Stripe, dopo aver effettuato l'accesso, attiva Visualizza dati di test .
  • In https://dashboard.stripe.com/test/apikeys, sostituisci YOUR_CREDENTIAL_HERE con i valori STRIPE_PUBLISHABLE_KEY e STRIPE_SECRET_KEY in /config/application.yml con il contenuto di Publishable Key pubblicabile e Chiave Secret key .

Passaggio 3: inizializza il modulo Stripe

Oltre a sostituire le chiavi, dobbiamo comunque inizializzare il modulo Stripe, in modo che utilizzi le chiavi già impostate nel nostro ENV .

Crea un file in config/initializers/stripe.rb con:

 Rails.application.configure do config.stripe.secret_key = ENV["STRIPE_SECRET_KEY"] config.stripe.publishable_key = ENV["STRIPE_PUBLISHABLE_KEY"] end

Passaggio 4: integra Stripe nel front-end.

Aggiungeremo la libreria JavaScript Stripe e la logica per l'invio di un token che rappresenta le informazioni sulla carta di credito dell'utente e verrà elaborato nel nostro back-end.

Nel file index.html.haml , aggiungilo all'inizio del tuo file. Questo utilizzerà il modulo Stripe (fornito dalla gem) per aggiungere la libreria javascript Stripe alla pagina dell'utente.

 = stripe_javascript_tag

Stripe utilizza campi di input protetti creati tramite la loro API. Poiché vengono creati in un iframe creato tramite questa API, non dovrai preoccuparti di possibili vulnerabilità nella gestione delle informazioni sulla carta di credito dell'utente. Inoltre, il tuo back-end non sarà in grado di elaborare/conservare dati sensibili dell'utente e riceverà solo un token che rappresenta queste informazioni.

Questi campi di input vengono creati chiamando stripe.elements().create('card') . Dopodiché è solo necessario chiamare l'oggetto restituito con mount() passando come argomento l'id/classe dell'elemento HTML su cui devono essere montati questi input. Maggiori informazioni possono essere trovate su Stripe.

Quando l'utente preme il pulsante di invio con il metodo di pagamento Stripe, un'altra chiamata API che restituisce una promessa viene eseguita sull'elemento della carta Stripe creato:

 stripe.createToken(card).then(function(result)

La variabile di result di questa funzione, se non ha un errore di proprietà assegnato, avrà un token che può essere recuperato accedendo all'attributo result.token.id . Questo token verrà inviato al back-end.

Per apportare queste modifiche, sostituisci il codice commentato // YOUR STRIPE AND PAYPAL CODE WILL BE HERE in index.html.haml con:

 (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

Se visiti la tua pagina, dovrebbe apparire come segue con i nuovi campi di input sicuri di Stripe:

Pagina indice integrata con i campi di input di Stripe secure.
Pagina indice integrata con i campi di input di Stripe secure.

Passaggio 5: verifica la tua applicazione.

Compila il modulo della carta di credito con una carta di prova (https://stripe.com/docs/testing) e invia la pagina. Verifica se l'azione di submit viene chiamata con tutti i parametri ( product_id , payment_gateway e token ) nell'output del server.

Spese a strisce

Gli addebiti stripe rappresentano transazioni una tantum. Pertanto, dopo una transazione di addebito Stripe, riceverai denaro direttamente dal cliente. Questo è l'ideale per vendere prodotti che non sono associati ai piani. In una sezione successiva, mostrerò come eseguire lo stesso tipo di transazione con PayPal, ma il nome di PayPal per questo tipo di transazione è Payment .

In questa sezione fornirò anche tutto lo scheletro per la gestione e l'inoltro di un ordine. Creiamo un ordine nell'azione di submit quando viene inviato il modulo Stripe. Questo ordine inizialmente avrà lo stato in sospeso , quindi se qualcosa va storto durante l'elaborazione dell'ordine, l'ordine sarà ancora in sospeso .

Se si verifica un errore dalle chiamate dell'API Stripe, impostiamo l'ordine in uno stato non riuscito e, se l'addebito viene completato correttamente, sarà nello stato pagato . L'utente viene anche reindirizzato in base alla risposta dell'API Stripe, come mostrato nel grafico seguente:

Transazioni a strisce.
Transazioni a strisce.

Inoltre, quando viene eseguito un addebito Stripe, viene restituito un ID. Conserveremo questo ID in modo che tu possa cercarlo in seguito nella dashboard di Stripe, se necessario. Questo ID può essere utilizzato anche se l'ordine deve essere rimborsato. Una cosa del genere non sarà esplorata in questo articolo.

Passaggio 1: crea il servizio Stripe.

Useremo una classe singleton per rappresentare le operazioni Stripe usando l'API Stripe. Per creare un addebito, viene chiamato il metodo Stripe::Charge.create e l'attributo dell'ID oggetto restituito verrà archiviato nel record dell'ordine charge_id . Questa funzione di create viene chiamata passando il token originato nel front-end, il prezzo dell'ordine e una descrizione.

Quindi, crea una nuova cartella app/services/orders e aggiungi un servizio Stripe: app/services/orders/stripe.rb contenente la classe Orders::Stripe singleton, che ha una voce nel metodo 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

Passaggio 2: implementa l'azione di invio e chiama il servizio Stripe.

In orders_controller.rb , aggiungi quanto segue nell'azione di submit , che in pratica chiamerà il servizio Orders::Stripe.execute . Si noti che sono state aggiunte anche due nuove funzioni private: prepare_new_order e 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

Passaggio 3: verifica la tua applicazione.

Verifica se l'azione di invio, quando viene chiamata con una scheda di test valida, esegue un reindirizzamento a un messaggio riuscito. Inoltre, controlla nella dashboard di Stripe se viene visualizzato anche l'ordine.

Abbonamenti a strisce

È possibile creare abbonamenti o piani per pagamenti ricorrenti. Con questo tipo di prodotti, l'utente paga automaticamente giornalmente, settimanalmente, mensilmente o annualmente in base alla configurazione del piano. In questa sezione, utilizzeremo il campo per product stripe_plan_name per memorizzare l'ID del piano, in realtà, è possibile per noi scegliere l'ID e lo chiameremo premium-plan , che verrà utilizzato per creare il relazione customer <-> subscription .

Creeremo anche una nuova colonna per la tabella degli utenti chiamata stripe_customer_id che verrà riempita con la proprietà id di un oggetto cliente Stripe. Un cliente Stripe viene creato quando viene chiamata la funzione Stripe::Customer.create e puoi anche controllare i clienti creati e collegati al tuo account in (https://dashboard.stripe.com/test/customers). I clienti vengono creati passando un parametro source che, nel nostro caso, è il token generato nel front end che viene inviato all'invio del form.

L'oggetto cliente ottenuto dall'ultima chiamata all'API Stripe menzionata viene utilizzato anche per creare un abbonamento che viene eseguito chiamando customer.subscriptions.create e passando l'ID del piano come parametro.

Inoltre, la gemma stripe-rails fornisce l'interfaccia per recuperare e aggiornare un cliente da Stripe, cosa che viene eseguita chiamando rispettivamente Stripe::Customer.retrieve e Stripe::Customer.update .

Quindi, quando un record utente ha già uno stripe_customer_id , invece di creare un nuovo cliente usando Stripe::Customer.create , chiameremo Stripe::Customer.retrieve passando lo stripe_customer_id come parametro, seguito da un Stripe::Customer.update e, in questo caso, passando al token un parametro.

Innanzitutto creeremo un piano utilizzando l'API Stripe in modo da poter creare un nuovo prodotto in abbonamento utilizzando il campo stripe_plan_name . Successivamente, apporteremo modifiche al servizio orders_controller e Stripe in modo da gestire la creazione e l'esecuzione degli abbonamenti Stripe.

Passaggio 1: crea un piano utilizzando l'API Stripe.

Apri la tua console usando i rails c . Crea un abbonamento per il tuo account Stripe con:

 Stripe::Plan.create({ amount: 10000, interval: 'month', product: { name: 'Premium plan', }, currency: 'usd', id: 'premium-plan', })

Se il risultato restituito in questo passaggio è vero, significa che il piano è stato creato correttamente e puoi accedervi nella dashboard di Stripe.

Passaggio 2: crea un prodotto nel database con il set di campi stripe_plan_name .

Ora crea un prodotto con stripe_plan_name impostato come premium-plan nel database:

 Product.create(price_cents: 10000, name: 'Premium Plan', stripe_plan_name: 'premium-plan')

Passaggio 3: genera una migrazione per aggiungere una colonna stripe_customer_id nella tabella degli users .

Eseguire quanto segue nel terminale:

 rails generate migration AddStripeCustomerIdToUser stripe_customer_id:string rails db:migrate

Passaggio 4: implementa la logica di abbonamento nella classe di servizio Stripe.

Aggiungi altre due funzioni nei metodi privati ​​di app/services/orders/stripe.rb : execute_subscription è responsabile della creazione degli abbonamenti nell'oggetto del cliente. La funzione find_or_create_customer è responsabile della restituzione del cliente già creato o della restituzione di un cliente appena creato.

 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

Infine, nella funzione di execute nello stesso file ( app/services/orders/stripe.rb ), chiameremo prima find_or_create_customer e quindi eseguiremo l'abbonamento chiamando execute_subscription passando il precedente cliente recuperato/creato. Quindi, sostituisci il commento #SUBSCRIPTIONS WILL BE HANDLED HERE nel metodo di execute con il seguente codice:

 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)

Passaggio 5: verifica la tua applicazione.

Visita il tuo sito web, seleziona il prodotto in abbonamento Premium Plan e compila una carta di prova valida. Dopo l'invio, dovrebbe reindirizzarti a una pagina di successo. Inoltre, controlla nella dashboard di Stripe se l'abbonamento è stato creato correttamente.

Configurazione PayPal

Come abbiamo fatto in Stripe, aggiungeremo anche una gemma per l'utilizzo dell'API PayPal: paypal-sdk-rest ed è anche richiesta la creazione di un account PayPal. Un flusso di lavoro descrittivo per PayPal che utilizza questa gemma può essere consultato nella documentazione ufficiale dell'API PayPal.

Passaggio 1: aggiungi la gemma paypal-sdk-rest al tuo progetto.

Aggiungi questo nel Gemfile :

 gem 'paypal-sdk-rest'

Correre:

 bundle install

Passaggio 2: genera le chiavi API.

Per avere le chiavi API per comunicare con PayPal, dovrai creare un conto PayPal. Così:

  • Crea un account (o usa il tuo conto PayPal) su https://developer.paypal.com/.
  • Ancora effettuato l'accesso al tuo account, crea due account sandbox su https://developer.paypal.com/developer/accounts/:
    • Personale (Account acquirente) – Verrà utilizzato nei test per effettuare pagamenti e abbonamenti.
    • Business (Account commerciante) – Questo sarà collegato all'applicazione, che avrà le chiavi API che stiamo cercando. Oltre a ciò, tutte le transazioni possono essere seguite in questo account.
  • Crea un'app su https://developer.paypal.com/developer/applications utilizzando l'account sandbox aziendale precedente.
  • Dopo questo passaggio, riceverai due chiavi per PayPal: Client ID e Secret .
  • In config/application.yml , sostituisci YOUR_CREDENTIAL_HERE da PAYPAL_CLIENT_ID e PAYPAL_CLIENT_SECRET con le chiavi che hai appena ricevuto.

Passaggio 3: inizializza il modulo PayPal.

Simile a Stripe, oltre a sostituire le chiavi in application.yml , dobbiamo ancora inizializzare il modulo PayPal in modo che possa utilizzare le chiavi già impostate nella nostra variabile ENV . A tale scopo, crea un file in config/initializers/paypal.rb con:

 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

Passaggio 4: integra PayPal nel front-end.

In index.html.haml aggiungi questo all'inizio del file:

 %script(src="https://www.paypal.com/sdk/js?client-id=#{ENV['PAYPAL_CLIENT_ID']}")

A differenza di Stripe, PayPal utilizza solo un pulsante che, quando viene cliccato, apre un popup sicuro in cui l'utente può accedere e procedere al pagamento/abbonamento. Questo pulsante può essere visualizzato chiamando il metodo paypal.Button(PARAM1).render(PARAM2) .

  • PARAM1 è un oggetto con la configurazione dell'ambiente e due funzioni di callback come proprietà: createOrder e onApprove .
  • PARAM2 indica l'identificatore dell'elemento HTML a cui allegare il pulsante PayPal.

Quindi, sempre nello stesso file, sostituisci il codice commentato YOUR PAYPAL CODE WILL BE HERE con:

 (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'); }());

Passaggio 5: verifica la tua applicazione.

Visita la tua pagina e controlla se il pulsante PayPal viene visualizzato quando selezioni PayPal come metodo di pagamento.

Transazioni PayPal

La logica per le transazioni PayPal, a differenza di Stripe, è un po' più complessa poiché coinvolge più richieste originate dal front-end al back-end. Ecco perché questa sezione esiste. Spiegherò più o meno (senza alcun codice) come verranno implementate le funzioni descritte nei metodi createOrder e onApprove e cosa ci si aspetta anche nei processi di back-end.

Passaggio 1: quando l'utente fa clic sul pulsante di invio PayPal, viene aperto un popup di PayPal che richiede le credenziali dell'utente ma in uno stato di caricamento. Viene chiamata la funzione di callback createOrder .

Popup PayPal, stato di caricamento
Popup PayPal, stato di caricamento

Passaggio 2: in questa funzione, eseguiremo una richiesta al nostro back-end che creerà un pagamento/abbonamento. Questo è l'inizio di una transazione e non verrà ancora applicato alcun addebito, quindi la transazione è effettivamente in uno stato in sospeso . Il nostro back-end dovrebbe restituirci un token, che verrà generato utilizzando il modulo PayPal (fornito tramite la gemma paypal-rest-sdk ).

Passaggio 3: ancora nella richiamata createOrder , restituiamo questo token generato nel nostro back-end e, se tutto è a posto, il pop-up di PayPal visualizzerà quanto segue, chiedendo le credenziali dell'utente:

Popup PayPal, credenziali utente
Popup PayPal, credenziali utente

Passaggio 4: dopo che l'utente ha effettuato l'accesso e selezionato il metodo di pagamento, il popup cambierà il suo stato nel seguente:

Popup PayPal, transazione autorizzata
Popup PayPal, transazione autorizzata

Passaggio 5: ora viene chiamata la funzione di callback onApprove . L'abbiamo definito come segue: onApprove: function(data) . L'oggetto data avrà le informazioni di pagamento per eseguirlo. In questo callback, questa volta verrà eseguita un'altra richiesta alla nostra funzione di back-end passando l'oggetto dati per eseguire l'ordine PayPal.

Passaggio 6: il nostro back-end esegue questa transazione e restituisce 200 (se ha esito positivo).

Passaggio 7: quando il nostro back-end ritorna, inviamo il modulo. Questa è la terza richiesta che facciamo al nostro back-end.

Nota che, a differenza di Stripe, ci sono tre richieste fatte al nostro back-end in questo processo. E manterremo il nostro stato di registrazione dell'ordine sincronizzato di conseguenza:

  • callback createOrder : viene creata una transazione e viene creato anche un record dell'ordine; pertanto, è in uno stato in sospeso come predefinito.
  • onApprove callback: la transazione viene eseguita e il nostro ordine verrà impostato come paypal_executed .
  • La pagina dell'ordine viene inviata: La transazione è stata già eseguita, quindi non cambia nulla. Il record dell'ordine cambierà il suo stato in pagato .

L'intero processo è descritto nel grafico seguente:

Transazioni PayPal
Transazioni PayPal

Pagamenti PayPal

I pagamenti PayPal seguono la stessa logica degli addebiti Stripe, quindi rappresentano transazioni una tantum, ma come accennato nella sezione precedente, hanno una logica di flusso diversa. Queste sono le modifiche che dovranno essere eseguite per la gestione dei pagamenti PayPal:

Passaggio 1: crea nuovi percorsi per PayPal ed esegui i pagamenti.

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 calling PayPal::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 through PayPal::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 call execute 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. con quanto segue:

 ... 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 method create_payment . If that returns successfully, it will return the order token created by Orders::Paypal.create_payment .
  • The paypal_execute_payment method: Will call our service method execute_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:

Index page after integration with PayPal
Index page after integration with PayPal

Step 6: Test your application.

  • Visita la pagina dell'indice.
  • Seleziona un prodotto di pagamento/addebito e PayPal come metodo di pagamento.
  • Fare clic sul pulsante Invia PayPal.
  • Nel popup di PayPal:
    • Utilizza le credenziali dell'account acquirente che hai creato.
    • Accedi e conferma il tuo ordine.
    • Il popup dovrebbe chiudersi.
  • Controlla se vieni reindirizzato a una pagina di successo.
  • Infine, controlla se l'ordine è stato eseguito sul conto PayPal accedendo con il tuo conto business su https://www.sandbox.paypal.com/signin e controllando la dashboard https://www.sandbox.paypal.com/listing /transazioni.

Abbonamenti PayPal

I piani/contratti/abbonamenti PayPal seguono la stessa logica degli abbonamenti Stripe e sono creati per pagamenti ricorrenti. Con questo tipo di prodotto l'utente paga automaticamente giornalmente, settimanalmente, mensilmente o annualmente secondo la sua configurazione.

Utilizzeremo il campo per il prodotto paypal_plan_name , al fine di memorizzare l'ID del piano fornito da PayPal. In questo caso, a differenza di Stripe, non scegliamo l'ID, e PayPal restituisce questo valore a cui verrà utilizzato per aggiornare l'ultimo prodotto creato nel nostro database.

Per creare un abbonamento, non sono richieste informazioni sul customer in nessun passaggio, poiché il metodo onApprove gestisce probabilmente questo collegamento nella sua implementazione sottostante. Quindi i nostri tavoli rimarranno gli stessi.

Passaggio 1: crea un piano utilizzando l'API PayPal.

Apri la tua console usando i rails c . Crea un abbonamento per il tuo conto PayPal con:

 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)

Passaggio 2: aggiorna l'ultimo prodotto nel database paypal_plan_name con il plan.id restituito.

Correre:

 Product.last.update(paypal_plan_name: plan.id)

Passaggio 3: aggiungi percorsi per l'abbonamento PayPal.

Aggiungi due nuovi percorsi in 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

Passaggio 4: gestisci la creazione e l'esecuzione nel servizio PayPal.

Aggiungi altre due funzioni per creare ed eseguire abbonamenti in 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

In create_subscription , inizializziamo un accordo chiamando il metodo PayPal::SDK::REST::Agreement.new e passando il product.paypal_plan_name come uno dei suoi attributi. Successivamente, lo creiamo e ora verrà impostato un token per quest'ultimo oggetto. Restituiamo anche il token al front-end.

In execute_subscription , troviamo il record order creato nella chiamata precedente. Dopodiché, inizializziamo un nuovo accordo, impostiamo il token di questo oggetto precedente e lo eseguiamo. Se quest'ultimo passaggio viene eseguito correttamente, lo stato dell'ordine viene impostato su paypal_executed . E ora torniamo al front-end l'ID accordo che è anche memorizzato in order.chager_id .

Passaggio 5: aggiungi azioni per creare ed eseguire abbonamenti in orders_controller .

Modifica app/controllers/orders_controller.rb . Nella parte superiore della classe, in primo luogo, e poi aggiorna il callback prepare_new_order da eseguire anche prima che venga chiamato paypal_create_subscription :

 class OrdersController < ApplicationController before_action :authenticate_user! before_action :prepare_new_order, only: [:paypal_create_payment, :paypal_create_subscription]

Inoltre, nello stesso file aggiungi le due funzioni pubbliche in modo che chiamino il servizio Orders::Paypal con un flusso simile a quello che abbiamo già nei pagamenti 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 ...

Passaggio 6: aggiunta di gestori di sottoscrizione per createOrder e onApprove callback nel front-end.

Infine, in index.html.haml , sostituisci la funzione paypal.Buttons con la seguente, che riempirà i due else vuoti che avevamo prima:

 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 creazione e l'esecuzione degli abbonamenti ha una logica simile a quella utilizzata per i pagamenti. Una differenza è che quando si eseguono pagamenti, i dati della funzione di callback onApprove hanno già un paymentID che rappresenta il charge_id per inviare il modulo tramite submitOrderPaypal(data.paymentID) . Per gli abbonamenti, otteniamo charge_id solo dopo averlo eseguito richiedendo un POST su paypal_execute_subscription_url , quindi possiamo chiamare submitOrderPaypal(executeData.id) .

Passaggio 7: verifica la tua applicazione.

  • Visita la pagina dell'indice.
  • Seleziona un prodotto in abbonamento e PayPal come metodo di pagamento.
  • Fare clic sul pulsante Invia PayPal.
  • Nel popup di PayPal:
    • Utilizza le credenziali dell'account acquirente che hai creato.
    • Accedi e conferma il tuo ordine.
    • Il popup dovrebbe chiudersi.
  • Controlla se vieni reindirizzato a una pagina di successo.
  • Infine controlla se l'ordine è stato eseguito sul conto PayPal accedendo con il tuo conto business su https://www.sandbox.paypal.com/signin e controllando la dashboard https://www.sandbox.paypal.com/listing/ transazioni.

Conclusione

Dopo aver letto questo articolo, dovresti essere in grado di integrare pagamenti/addebiti e transazioni di abbonamenti per PayPal e Stripe nella tua applicazione Rails. Ci sono molti punti che potrebbero essere migliorati che non ho aggiunto in questo articolo per motivi di brevità. Ho organizzato il tutto partendo da un presupposto di difficoltà:

  • Più facile:
    • Utilizza Transport Layer Security (TLS) in modo che le tue richieste utilizzino HTTPS.
    • Implementa le configurazioni dell'ambiente di produzione sia per PayPal che per Stripe.
    • Aggiungi una nuova pagina in modo che gli utenti possano accedere a una cronologia degli ordini precedenti.
  • Medio:
    • Rimborsa o cancella gli abbonamenti.
    • Fornire una soluzione per i pagamenti degli utenti non registrati.
  • Più forte:
    • Fornisci un modo per rimuovere gli account e conservare il loro token e customer_id se l'utente desidera tornare. Ma dopo un certo numero di giorni, rimuovi questi dati in modo che la tua applicazione sia più conforme allo standard PCI.
    • Passa all'API PayPal versione 2 lato server (https://developer.paypal.com/docs/api/payments/v2/) La gemma che abbiamo usato in questo tutorial paypal-sdk-rest , ha solo una versione beta per la versione 2, in modo che possa essere utilizzato con cautela (https://github.com/paypal/PayPal-Ruby-SDK/tree/2.0-beta).
    • Includi richieste idempotenti.
      • Striscia: https://stripe.com/docs/api/idempotent_requests
      • PayPal: https://developer.paypal.com/docs/api-basics/#api-idempotency

Consiglio anche di leggere l'elemento Stripe Checkout, che è un altro modo per integrare Stripe nel front-end. A differenza di Stripe Elements, che abbiamo utilizzato in questo tutorial, Stripe Checkout apre un popup dopo aver fatto clic su un pulsante (simile a PayPal) in cui l'utente inserisce i dati della carta di credito OPPURE sceglie di pagare con Google Pay/Apple Pay https://stripe.com /documenti/web.

Una seconda raccomandazione di lettura sono le pagine di sicurezza per entrambi i gateway di pagamento.

  • Per Stripe
  • Per PayPal

Infine, grazie per aver letto questo articolo! Puoi anche controllare il mio progetto GitHub utilizzato per questo esempio di progetto. Lì, ho aggiunto anche i test rspec durante lo sviluppo.