Integration von Stripe- und PayPal-Zahlungsmethoden in Ruby on Rails

Veröffentlicht: 2022-03-11

Ein Schlüsselmerkmal für große E-Commerce-Unternehmen wie AliExpress, Ebay und Amazon ist eine sichere Art der Zahlungsabwicklung, die für ihr Geschäft unerlässlich ist. Fällt diese Funktion aus, wären die Folgen verheerend. Dies gilt für Branchenführer und Ruby on Rails-Entwickler, die an E-Commerce-Apps arbeiten.

Cybersicherheit ist unerlässlich, um Angriffe zu verhindern, und eine Möglichkeit, den Transaktionsprozess sicherer zu machen, besteht darin, einen Drittanbieterdienst damit zu beauftragen. Das Einbinden von Zahlungs-Gateways in Ihre Anwendung ist eine Möglichkeit, dieses Ziel zu erreichen, da sie Benutzerautorisierung, Datenverschlüsselung und ein Dashboard bieten, damit Sie den Transaktionsstatus im Handumdrehen verfolgen können.

Es gibt eine Vielzahl von Zahlungs-Gateway-Diensten im Internet, aber in diesem Artikel werde ich mich auf die Integration von Stripe und PayPal in eine Rails-Anwendung konzentrieren. Um nur einige andere zu nennen: Amazon Payments, Square, SecurePay, WorldPay, Authorize.Net, 2Checkout.com, Braintree, Amazon oder BlueSnap.

Funktionsweise der Payment Gateway-Integration

Allgemeine Vertretung für Transaktionen mit Payment Gateways
Allgemeine Vertretung für Transaktionen mit Payment Gateways

Im Allgemeinen wird es in Ihrer Anwendung ein Formular/eine Schaltfläche geben, wo sich der Benutzer anmelden/Kreditkartendaten eingeben kann. PayPal und Stripe machen diesen ersten Schritt bereits sicherer, indem sie iframe -Formulare oder popups verwenden, die verhindern, dass Ihre Anwendung sensible Kreditkarteninformationen des Benutzers speichert, da sie ein Token zurückgeben, das diese Transaktion darstellt. Einige Benutzer fühlen sich möglicherweise auch sicherer, Zahlungen zu verarbeiten, wenn sie wissen, dass ein Drittanbieter den Transaktionsprozess abwickelt, sodass dies auch ein Anreiz für Ihre Anwendung sein kann.

Nach der Authentifizierung der Benutzerinformationen bestätigt ein Zahlungs-Gateway die Zahlung, indem es einen Zahlungsabwickler kontaktiert, der mit Banken kommuniziert, um Zahlungen abzuwickeln. Dadurch wird sichergestellt, dass die Transaktion ordnungsgemäß belastet/gutgeschrieben wird.

Stripe verwendet ein Kreditkartenformular, in dem Kreditkartennummer, CVV und Ablaufdatum abgefragt werden. Der Benutzer muss also Kreditkarteninformationen in die gesicherten Stripe-Eingaben eingeben. Nachdem Sie diese Informationen bereitgestellt haben, verarbeitet Ihr Anwendungs-Back-End diese Zahlung über ein Token.

Im Gegensatz zu Stripe leitet PayPal den Benutzer auf die PayPal-Anmeldeseite weiter. Der Benutzer autorisiert und wählt die Zahlungsmethode über PayPal aus, und auch hier verarbeitet Ihr Backend Token anstelle von sensiblen Benutzerdaten.

Es ist wichtig zu erwähnen, dass Ihr Back-End für diese beiden Zahlungs-Gateways die Fortsetzung der Transaktionsausführung über Stripe- oder PayPal-APIs anfordern sollte, die eine OK/NOK-Antwort geben, sodass Ihre Anwendung den Benutzer entsprechend auf eine Erfolgs- oder Fehlerseite umleiten sollte.

Die Absicht dieses Artikels ist es, eine Kurzanleitung für die Integration dieser beiden Zahlungs-Gateways in einer einzigen Anwendung bereitzustellen. Für alle Tests verwenden wir Sandboxes und Testkonten, die von Stripe und PayPal bereitgestellt werden, um Zahlungen zu simulieren.

Aufstellen

Vor der Integration von Zahlungs-Gateways führen wir ein Setup zum Initialisieren der Anwendung durch Hinzufügen von Edelsteinen, Datenbanktabellen und einer Indexseite durch. Dieses Projekt wurde mit Rails Version 5.2.3 und Ruby 2.6.3 erstellt.

Hinweis: Sie können sich die neuen Rails 6-Funktionen in unserem letzten Artikel ansehen.

Schritt 1: Initialisieren Sie eine Rails-Anwendung.

Initialisieren Sie das Projekt, indem Sie die Projektinitialisierung mit dem Befehl rails mit Ihrem App-Namen ausführen:

 rails new YOUR_APP_NAME

Und cd in Ihrem Bewerbungsordner.

Schritt 2: Installieren Sie Edelsteine.

Neben Stripe- und PayPal-Edelsteinen wurden einige weitere Edelsteine ​​​​hinzugefügt:

  • devise : Wird für die Benutzerauthentifizierung und -autorisierung verwendet
  • haml : Templating-Tool zum Rendern von Benutzerseiten
  • jquery-rails : für jquery in den Front-End-Skripten
  • money-rails : zum Anzeigen von formatierten Geldwerten

Zu Ihrem Gemfile :

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

Führen Sie nach dem Hinzufügen in Ihrer CLI Folgendes aus:

 bundle install

Schritt 3: Edelsteine ​​initialisieren.

Einige dieser Gems erfordern neben der Installation über bundle eine Initialisierung.

Gerät installieren:

 rails g devise:install

money-rails initialisieren :

 rails g money_rails:initializer

Initialisieren jquery-rails indem Sie am Ende von app/assets/javascripts/application.js Folgendes anhängen:

 //= require jquery //= require jquery_ujs

Schritt 4: Tabellen und Migrationen

In diesem Projekt werden drei Tabellen verwendet: Benutzer , Produkte und Bestellungen .

  • Users : Wird durch devise generiert
  • Products :
    • name
    • price_cents
    • Stripe_plan_name : Eine ID, die einen in Stripe erstellten Abonnementplan darstellt, damit Benutzer ihn abonnieren können. Dieses Feld ist nur für Produkte erforderlich, die einem Stripe-Plan zugeordnet sind.
    • paypal_plan_name : Dasselbe wie stripe_plan_name , aber für PayPal
  • Orders :
    • product_id
    • user_id
    • status : Dies informiert, ob die Bestellung aussteht, fehlgeschlagen oder bezahlt ist.
    • token : Dies ist ein Token, das von den APIs (entweder Stripe oder PayPal) generiert wird, um eine Transaktion zu initialisieren.
    • price_cents : Ähnlich wie das Produkt, aber verwendet, um diesen Wert im Bestelldatensatz dauerhaft zu machen
    • payment_gateway : Speichert welches Payment Gateway für die Bestellung PayPal oder Stripe verwendet wird
    • customer_id : Diese wird für Stripe verwendet, um den Stripe-Kunden für ein Abonnement zu speichern, und wird in einem späteren Abschnitt ausführlicher erläutert.

Um diese Tabellen zu generieren, müssen einige Migrationen generiert werden:

Zum Erstellen der Benutzertabelle . Laufen:

 rails g devise User

Zum Erstellen der Produkttabelle . Generieren Sie eine Migration, indem Sie Folgendes ausführen:

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

Öffnen Sie Ihre erstellte Migrationsdatei, die sich unter db/migrate/ befinden sollte, und nehmen Sie Änderungen vor, damit Ihre Migration ungefähr so ​​aussieht:

 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

Zum Erstellen der Orders-Tabelle . Generieren Sie eine Migration, indem Sie Folgendes ausführen:

 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

Öffnen Sie erneut Ihre erstellte Migrationsdatei, die sich unter db/migrate/ befinden sollte, und nehmen Sie Änderungen an dieser Datei vor, damit sie ungefähr so ​​aussieht:

 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

Führen Sie Datenbankmigrationen durch, indem Sie Folgendes ausführen:

 rails db:migrate

Schritt 5: Erstellen Sie Modelle.

Das Benutzermodell wurde bereits bei der Geräteinstallation erstellt, und es sind keine Änderungen daran erforderlich. Außerdem werden zwei Modelle für Product und Order erstellt.

Produkt. Fügen Sie eine neue Datei hinzu, app/models/product.rb , mit:

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

Befehl. Fügen Sie eine neue Datei hinzu, app/models/order.rb , mit:

 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

Schritt 6: Befüllen Sie die Datenbank.

In der Konsole werden ein Benutzer und zwei Produkte erstellt. Bestelldatensätze werden gemäß Zahlungstests erstellt.

  • rails s
  • Rufen Sie in Ihrem Browser http://localhost:3000 auf
  • Sie werden auf eine Anmeldeseite weitergeleitet.
  • Registrieren Sie einen Benutzer, indem Sie seine E-Mail-Adresse und sein Passwort eingeben.
  • In Ihrem Terminal werden die folgenden Protokolle angezeigt, die zeigen, dass ein Benutzer in Ihrer Datenbank erstellt wurde:
 User Create (0.1ms) INSERT INTO "users" ("email", "encrypted_password", "created_at", "updated_at") VALUES (?, ?, ?, ?) …
  • Erstellen Sie zwei Produkte ohne Abonnements, indem Sie rails c und Folgendes hinzufügen:
    • Product.create(name: "Awesome T-Shirt", price_cents: 3000)
    • Product.create(name: "Awesome Sneakers", price_cents: 5000)

Schritt 7: Erstellen Sie eine Indexseite

Die Hauptseite für das Projekt enthält eine Produktauswahl für Käufe oder Abonnements. Darüber hinaus gibt es einen Abschnitt zur Auswahl der Zahlungsmethode (Stripe oder PayPal). Für jeden Zahlungs-Gateway-Typ wird auch eine Senden-Schaltfläche verwendet, da wir für PayPal ein eigenes Schaltflächendesign über seine JavaScript-Bibliothek hinzufügen werden.

Erstellen Sie zuerst die Routen für den index und submit Sie sie in config/routes.rb .

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

index erstellen und hinzufügen und im Auftragscontroller app/controllers/orders_controller.rb submit Die Aktion „ orders#index “ speichert zwei Variablen, die im Front-End verwendet werden sollen: @products_purchase mit einer Liste von Produkten ohne Pläne und @products_subscription mit Produkten mit PayPal- und Stripe-Plänen.

 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

Erstellen Sie eine Datei in app/views/orders/index.html.haml . Diese Datei enthält alle Eingaben, die wir über die Übermittlungsmethode an unser Backend senden, sowie die Interaktion für Zahlungsgateways und Produktauswahl. Hier sind einige Eingabenamensattribute:

  • Orders[product_id] speichert die Produkt-ID.
  • Orders[payment_gateway] enthält das Zahlungsgateway mit entweder Stripe- oder PayPal-Werten für das andere.
 %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; }

Wenn Sie Ihre Anwendung mit rails s ausführen und Ihre Seite in http://localhost:3000 besuchen. Sie sollten die Seite wie folgt sehen können:

Rohindexseite ohne Stripe- und PayPal-Integration
Rohindexseite ohne Stripe- und PayPal-Integration

Speicherung von Anmeldeinformationen für das Zahlungsgateway

PayPal- und Stripe-Schlüssel werden in einer Datei gespeichert, die nicht von Git verfolgt wird. In dieser Datei sind für jedes Zahlungs-Gateway zwei Arten von Schlüsseln gespeichert, für die wir vorerst einen Dummy-Wert verwenden. Weitere Anweisungen zum Erstellen dieser Schlüssel werden in weiteren Abschnitten vorgestellt.

Schritt 1: Fügen Sie dies in .gitignore .

 /config/application.yml

Schritt 2: Erstellen Sie eine Datei mit Ihren Anmeldeinformationen in config/application.yml . Es sollte alle Ihre PayPal- und Stripe-Sandbox-/Testschlüssel für den Zugriff auf diese APIs enthalten.

 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

Schritt 3: Um die Variablen aus der Datei config/application.yml beim Start der Anwendung zu speichern, fügen Sie diese Zeilen in config/application.rb innerhalb der Application -Klasse hinzu, damit sie in ENV verfügbar sind.

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

Stripe-Konfiguration

Wir werden ein Juwel für die Verwendung der Stripe-API hinzufügen: stripe-rails . Das Erstellen eines Stripe-Kontos ist ebenfalls erforderlich, damit Gebühren und Abonnements verarbeitet werden können. Wenn Sie müssen, können Sie die API-Methoden für die Stripe-API in der offiziellen Dokumentation einsehen.

Schritt 1: Fügen Sie Ihrem Projekt das Stripe-Rails-Juwel hinzu.

Das Stripe-Rails-Gem wird eine Schnittstelle für alle in diesem Projekt verwendeten API-Anforderungen bereitstellen.

Fügen Sie dies in die Gemfile :

 gem 'stripe-rails'

Laufen:

 bundle install

Schritt 2: Generieren Sie Ihre API-Schlüssel.

Um die API-Schlüssel für die Kommunikation mit Stripe zu haben, müssen Sie ein Konto in Stripe erstellen. Um die Anwendung zu testen, ist es möglich, den Testmodus zu verwenden, sodass bei der Erstellung eines Stripe-Kontos keine echten Geschäftsinformationen ausgefüllt werden müssen.

  • Erstellen Sie ein Konto in Stripe, falls Sie noch keines haben (https://dashboard.stripe.com/).
  • Schalten Sie, während Sie sich noch im Stripe-Dashboard befinden, nach der Anmeldung Testdaten anzeigen ein.
  • Ersetzen Sie unter https://dashboard.stripe.com/test/apikeys YOUR_CREDENTIAL_HERE für die Werte STRIPE_PUBLISHABLE_KEY und STRIPE_SECRET_KEY in /config/application.yml durch den Inhalt von Publishable Key und Secret key .

Schritt 3: Stripe-Modul initialisieren

Zusätzlich zum Ersetzen der Schlüssel müssen wir das Stripe-Modul noch initialisieren, damit es die bereits in unserem ENV festgelegten Schlüssel verwendet.

Erstellen Sie eine Datei in config/initializers/stripe.rb mit:

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

Schritt 4: Integrieren Sie Stripe in das Frontend.

Wir werden die Stripe-JavaScript-Bibliothek und die Logik zum Senden eines Tokens hinzufügen, das die Kreditkarteninformationen des Benutzers darstellt und in unserem Backend verarbeitet wird.

Fügen Sie dies in der Datei index.html.haml am Anfang Ihrer Datei hinzu. Dadurch wird das Stripe-Modul (vom Gem bereitgestellt) verwendet, um die Stripe-Javascript-Bibliothek zur Seite des Benutzers hinzuzufügen.

 = stripe_javascript_tag

Stripe verwendet sichere Eingabefelder, die über ihre API erstellt werden. Da sie in einem über diese API erstellten iframe erstellt werden, müssen Sie sich keine Gedanken über mögliche Schwachstellen bei der Verarbeitung von Kreditkarteninformationen von Benutzern machen. Darüber hinaus kann Ihr Back-End keine sensiblen Benutzerdaten verarbeiten/speichern und erhält nur ein Token, das diese Informationen darstellt.

Diese Eingabefelder werden durch Aufrufen von stripe.elements().create('card') erstellt. Danach muss nur noch das zurückgegebene Objekt mit mount() aufgerufen werden, indem als Argument das HTML-Element id/class übergeben wird, in das diese Eingaben gemountet werden sollen. Weitere Informationen finden Sie bei Stripe.

Wenn der Benutzer mit der Stripe-Zahlungsmethode auf die Schaltfläche „Senden“ klickt, wird ein weiterer API-Aufruf, der ein Versprechen zurückgibt, für das erstellte Stripe-Kartenelement ausgeführt:

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

Die result dieser Funktion hat, wenn ihr kein Eigenschaftsfehler zugewiesen ist, ein Token, das durch Zugriff auf das Attribut result.token.id abgerufen werden kann. Dieses Token wird an das Backend gesendet.

Um diese Änderungen vorzunehmen, ersetzen Sie den kommentierten Code // YOUR STRIPE AND PAYPAL CODE WILL BE HERE in index.html.haml durch:

 (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

Wenn Sie Ihre Seite besuchen, sollte sie mit den neuen sicheren Stripe-Eingabefeldern wie folgt aussehen:

Indexseite integriert mit sicheren Stripe-Eingabefeldern.
Indexseite integriert mit sicheren Stripe-Eingabefeldern.

Schritt 5: Testen Sie Ihre Anwendung.

Füllen Sie das Kreditkartenformular mit einer Testkarte aus (https://stripe.com/docs/testing) und senden Sie die Seite ab. Überprüfen Sie, ob die submit -Aktion mit allen Parametern ( product_id , payment_gateway und token ) in Ihrer Serverausgabe aufgerufen wird.

Stripe-Gebühren

Stripe-Gebühren stellen einmalige Transaktionen dar. Daher würden Sie nach einer Stripe-Aufladungstransaktion direkt Geld vom Kunden erhalten. Dies ist ideal für den Verkauf von Produkten, die nicht mit Plänen verbunden sind. In einem späteren Abschnitt werde ich zeigen, wie man denselben Transaktionstyp mit PayPal durchführt, aber der Name von PayPal für diesen Transaktionstyp ist Payment .

In diesem Abschnitt werde ich auch das gesamte Skelett für die Bearbeitung und Übermittlung einer Bestellung bereitstellen. Wir erstellen eine Bestellung in der submit , wenn das Stripe-Formular gesendet wird. Diese Bestellung hat zunächst den Status „ ausstehend “. Wenn also bei der Bearbeitung dieser Bestellung etwas schief geht, bleibt die Bestellung noch ausstehend .

Wenn bei Stripe-API-Aufrufen ein Fehler auftritt, setzen wir die Bestellung in einen fehlgeschlagenen Zustand, und wenn die Belastung erfolgreich abgeschlossen wurde, befindet sie sich im bezahlten Zustand. Der Benutzer wird auch gemäß der Stripe-API-Antwort umgeleitet, wie in der folgenden Grafik dargestellt:

Stripe-Transaktionen.
Stripe-Transaktionen.

Wenn eine Stripe-Gebühr durchgeführt wird, wird außerdem eine ID zurückgegeben. Wir speichern diese ID, damit Sie später bei Bedarf in Ihrem Stripe-Dashboard danach suchen können. Diese ID kann auch verwendet werden, wenn die Bestellung zurückerstattet werden muss. So etwas wird in diesem Artikel nicht untersucht.

Schritt 1: Erstellen Sie den Stripe-Dienst.

Wir werden eine Singleton-Klasse verwenden, um Stripe-Operationen mit der Stripe-API darzustellen. Um eine Gebühr zu erstellen, wird die Methode Stripe::Charge.create aufgerufen, und das zurückgegebene Objekt-ID-Attribut wird im Auftragsdatensatz charge_id gespeichert. Diese create wird aufgerufen, indem das aus dem Frontend stammende Token, der Bestellpreis und eine Beschreibung übergeben werden.

Erstellen Sie also einen neuen Ordner app/services/orders und fügen Sie einen Stripe-Dienst hinzu: app/services/orders/stripe.rb , der die Singleton-Klasse Orders::Stripe enthält, die einen Eintrag in der Methode execute hat.

 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

Schritt 2: Implementieren Sie die Submit-Aktion und rufen Sie den Stripe-Dienst auf.

Fügen Sie in orders_controller.rb Folgendes in der submit -Aktion hinzu, die im Grunde den Dienst Orders::Stripe.execute . Beachten Sie, dass auch zwei neue private Funktionen hinzugefügt wurden: prepare_new_order und 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

Schritt 3: Testen Sie Ihre Anwendung.

Überprüfen Sie, ob die Submit-Aktion, wenn sie mit einer gültigen Testkarte aufgerufen wird, eine Umleitung zu einer erfolgreichen Nachricht durchführt. Überprüfen Sie außerdem in Ihrem Stripe-Dashboard, ob die Bestellung ebenfalls angezeigt wird.

Stripe-Abonnements

Abonnements oder Pläne können für wiederkehrende Zahlungen erstellt werden. Bei dieser Art von Produkt wird dem Benutzer je nach Plankonfiguration täglich, wöchentlich, monatlich oder jährlich automatisch eine Rechnung gestellt. In diesem Abschnitt verwenden wir das Feld für das Produkt stripe_plan_name , um die Plan-ID zu speichern – eigentlich ist es uns möglich, die ID auszuwählen, und wir nennen sie premium-plan –, die verwendet wird, um die zu erstellen Beziehung customer <-> subscription .

Wir werden auch eine neue Spalte für die Benutzertabelle namens stripe_customer_id erstellen, die mit der ID-Eigenschaft eines Stripe-Kundenobjekts gefüllt wird. Ein Stripe-Kunde wird erstellt, wenn die Funktion Stripe::Customer.create aufgerufen wird, und Sie können die erstellten und mit Ihrem Konto verknüpften Kunden auch unter (https://dashboard.stripe.com/test/customers) überprüfen. Kunden werden erstellt, indem ein source übergeben wird, der in unserem Fall das im Frontend generierte Token ist, das beim Absenden des Formulars gesendet wird.

Das vom zuletzt erwähnten Stripe-API-Aufruf erhaltene Kundenobjekt wird auch zum Erstellen eines Abonnements verwendet, was durch Aufrufen von customer.subscriptions.create und Übergeben der Plan-ID als Parameter erfolgt.

Darüber hinaus bietet das stripe-rails Gem die Schnittstelle zum Abrufen und Aktualisieren eines Kunden von Stripe, was durch Aufrufen von Stripe::Customer.retrieve bzw. Stripe::Customer.update erfolgt.

Wenn also ein Benutzerdatensatz bereits eine stripe_customer_id hat, rufen wir Stripe::Customer.retrieve auf, anstatt einen neuen Kunden mit Stripe::Customer.create zu erstellen, indem wir die stripe_customer_id als Parameter übergeben, gefolgt von einem Stripe::Customer.update , und in diesem Fall wird dem Token ein Parameter übergeben.

Zuerst erstellen wir einen Plan mit der Stripe-API, damit wir ein neues Abonnementprodukt mit dem Feld stripe_plan_name können. Anschließend werden wir Änderungen orders_controller und im Stripe-Dienst vornehmen, damit die Erstellung und Ausführung von Stripe-Abonnements gehandhabt wird.

Schritt 1: Erstellen Sie einen Plan mit der Stripe-API.

Öffnen Sie Ihre Konsole über die Befehlsleiste rails c . Erstellen Sie ein Abonnement für Ihr Stripe-Konto mit:

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

Wenn das zurückgegebene Ergebnis in diesem Schritt wahr ist, bedeutet dies, dass der Plan erfolgreich erstellt wurde und Sie in Ihrem Stripe-Dashboard darauf zugreifen können.

Schritt 2: Erstellen Sie ein Produkt in der Datenbank mit dem stripe_plan_name .

Erstellen Sie nun ein Produkt mit dem stripe_plan_name als premium-plan in der Datenbank:

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

Schritt 3: Generieren Sie eine Migration zum Hinzufügen einer Spalte stripe_customer_id in der users .

Führen Sie im Terminal Folgendes aus:

 rails generate migration AddStripeCustomerIdToUser stripe_customer_id:string rails db:migrate

Schritt 4: Implementieren Sie die Abonnementlogik in der Stripe-Dienstklasse.

Fügen Sie zwei weitere Funktionen in den privaten Methoden von app/services/orders/stripe.rb : execute_subscription ist für die Erstellung der Abonnements im Objekt des Kunden verantwortlich. Die Funktion find_or_create_customer ist dafür verantwortlich, den bereits erstellten Kunden zurückzugeben oder einen neu erstellten Kunden zurückzugeben.

 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

Schließlich rufen wir in der execute in derselben Datei ( app/services/orders/stripe.rb ) zuerst find_or_create_customer und führen dann das Abonnement aus, indem wir execute_subscription aufrufen, indem wir den zuvor abgerufenen/erstellten Kunden übergeben. Ersetzen Sie also den Kommentar #SUBSCRIPTIONS WILL BE HANDLED HERE in der execute -Methode durch den folgenden Code:

 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)

Schritt 5: Testen Sie Ihre Anwendung.

Besuchen Sie Ihre Website, wählen Sie das Abonnementprodukt Premium Plan aus und füllen Sie eine gültige Testkarte aus. Nach dem Absenden sollte es Sie zu einer erfolgreichen Seite weiterleiten. Überprüfen Sie außerdem in Ihrem Stripe-Dashboard, ob das Abonnement erfolgreich erstellt wurde.

PayPal-Konfiguration

Wie wir es in Stripe getan haben, werden wir auch ein Juwel für die Verwendung der PayPal-API hinzufügen: paypal-sdk-rest , und die Erstellung eines PayPal-Kontos ist ebenfalls erforderlich. Ein beschreibender Workflow für PayPal, der dieses Juwel verwendet, kann in der offiziellen PayPal-API-Dokumentation eingesehen werden.

Schritt 1: Fügen Sie Ihrem Projekt das Juwel paypal-sdk-rest .

Fügen Sie dies in die Gemfile :

 gem 'paypal-sdk-rest'

Laufen:

 bundle install

Schritt 2: Generieren Sie Ihre API-Schlüssel.

Um die API-Schlüssel für die Kommunikation mit PayPal zu erhalten, müssen Sie ein PayPal-Konto erstellen. Damit:

  • Erstellen Sie ein Konto (oder verwenden Sie Ihr PayPal-Konto) unter https://developer.paypal.com/.
  • Immer noch in Ihrem Konto angemeldet, erstellen Sie zwei Sandbox-Konten unter https://developer.paypal.com/developer/accounts/:
    • Persönlich (Käuferkonto) – Dies wird in Ihren Tests für Zahlungen und Abonnements verwendet.
    • Geschäftlich (Händlerkonto) – Dies wird mit der Anwendung verknüpft, die die gesuchten API-Schlüssel enthält. Außerdem können alle Transaktionen in diesem Konto verfolgt werden.
  • Erstellen Sie eine App unter https://developer.paypal.com/developer/applications mit dem vorherigen Business-Sandbox-Konto.
  • Nach diesem Schritt erhalten Sie zwei Schlüssel für PayPal: Client ID und Secret .
  • Ersetzen Sie in config/application.yml YOUR_CREDENTIAL_HERE von PAYPAL_CLIENT_ID und PAYPAL_CLIENT_SECRET durch die Schlüssel, die Sie gerade erhalten haben.

Schritt 3: Initialisieren Sie das PayPal-Modul.

Ähnlich wie bei Stripe müssen wir neben dem Ersetzen der Schlüssel in application.yml noch das PayPal-Modul initialisieren, damit es die bereits in unserer ENV -Variablen festgelegten Schlüssel verwenden kann. Erstellen Sie dazu eine Datei in config/initializers/paypal.rb mit:

 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

Schritt 4: PayPal im Frontend integrieren.

Fügen Sie in index.html.haml dies am Anfang der Datei hinzu:

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

Im Gegensatz zu Stripe verwendet PayPal nur eine Schaltfläche, die, wenn sie angeklickt wird, ein sicheres Popup öffnet, in dem sich der Benutzer anmelden und mit der Zahlung/dem Abonnement fortfahren kann. Dieser Button kann durch Aufrufen der Methode paypal.Button(PARAM1).render(PARAM2) werden.

  • PARAM1 ist ein Objekt mit der Umgebungskonfiguration und zwei Callback-Funktionen als Eigenschaften: createOrder und onApprove .
  • PARAM2 gibt die HTML-Elementkennung an, an die die PayPal-Schaltfläche angehängt werden soll.

Ersetzen Sie also immer noch in derselben Datei den kommentierten Code YOUR PAYPAL CODE WILL BE HERE durch:

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

Schritt 5: Testen Sie Ihre Anwendung.

Besuchen Sie Ihre Seite und überprüfen Sie, ob die PayPal-Schaltfläche angezeigt wird, wenn Sie PayPal als Zahlungsmethode auswählen.

PayPal-Transaktionen

Die Logik für PayPal-Transaktionen ist im Gegensatz zu Stripe etwas komplexer, da mehr Anfragen vom Front-End zum Back-End gehören. Deshalb gibt es diese Rubrik. Ich werde mehr oder weniger (ohne Code) erklären, wie die in den createOrder und onApprove Methoden beschriebenen Funktionen implementiert werden und was auch in den Backend-Prozessen erwartet wird.

Schritt 1: Wenn der Benutzer auf die PayPal-Senden-Schaltfläche klickt, ist ein PayPal-Popup geöffnet, das nach Benutzerdaten fragt, aber in einem Ladezustand. Der Funktionsrückruf createOrder wird aufgerufen.

PayPal-Popup, Ladezustand
PayPal-Popup, Ladezustand

Schritt 2: In dieser Funktion führen wir eine Anfrage an unser Backend durch, die eine Zahlung/ein Abonnement erstellt. Dies ist der allererste Beginn einer Transaktion, und es werden noch keine Gebühren erhoben, sodass sich die Transaktion tatsächlich in einem ausstehenden Zustand befindet. Unser Back-End sollte uns ein Token zurückgeben, das mit dem PayPal-Modul generiert wird (bereitgestellt durch das Juwel paypal-rest-sdk ).

Schritt 3: Immer noch im createOrder Callback geben wir dieses Token zurück, das in unserem Back-End generiert wurde, und wenn alles in Ordnung ist, zeigt das PayPal-Popup Folgendes an und fragt nach Benutzerdaten:

PayPal-Popup, Benutzeranmeldeinformationen
PayPal-Popup, Benutzeranmeldeinformationen

Schritt 4: Nachdem sich der Benutzer angemeldet und die Zahlungsmethode ausgewählt hat, ändert das Popup seinen Status wie folgt:

PayPal-Popup, autorisierte Transaktion
PayPal-Popup, autorisierte Transaktion

Schritt 5: Der Callback der Funktion onApprove wird nun aufgerufen. Wir haben es wie folgt definiert: onApprove: function(data) . Das data wird die Zahlungsinformationen haben, um es auszuführen. Bei diesem Rückruf wird eine weitere Anfrage an unsere Backend-Funktion durchgeführt, diesmal unter Weitergabe des Datenobjekts, um die PayPal-Bestellung auszuführen.

Schritt 6: Unser Backend führt diese Transaktion aus und gibt 200 zurück (falls erfolgreich).

Schritt 7: Wenn unser Backend zurückkehrt, senden wir das Formular ab. Dies ist die dritte Anfrage, die wir an unser Backend stellen.

Beachten Sie, dass im Gegensatz zu Stripe in diesem Prozess drei Anfragen an unser Back-End gestellt werden. Und wir werden unseren Bestellstatus entsprechend synchronisieren:

  • createOrder Callback: Eine Transaktion wird erstellt und ein Bestelldatensatz wird ebenfalls erstellt; daher befindet es sich standardmäßig in einem ausstehenden Zustand.
  • onApprove Callback: Die Transaktion wird ausgeführt und unsere Bestellung wird als paypal_executed gesetzt.
  • Die Bestellseite wird übermittelt: Die Transaktion wurde bereits ausgeführt, es ändert sich also nichts. Der Bestelldatensatz ändert seinen Status in bezahlt .

Dieser gesamte Prozess wird in der folgenden Grafik beschrieben:

PayPal-Transaktionen
PayPal-Transaktionen

PayPal-Zahlungen

PayPal-Zahlungen folgen derselben Logik wie Stripe-Gebühren, stellen also einmalige Transaktionen dar, aber wie im vorherigen Abschnitt erwähnt, haben sie eine andere Ablauflogik. Dies sind die Änderungen, die für die Abwicklung von PayPal-Zahlungen vorgenommen werden müssen:

Schritt 1: Erstellen Sie neue Routen für PayPal und führen Sie Zahlungen aus.

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. mit den folgenden:

 ... 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.

  • Besuchen Sie die Indexseite.
  • Wählen Sie ein Zahlungs-/Aufladeprodukt und PayPal als Zahlungsmethode aus.
  • Klicken Sie auf die Schaltfläche „PayPal senden“.
  • Im PayPal-Popup:
    • Verwenden Sie die Anmeldeinformationen für das von Ihnen erstellte Käuferkonto.
    • Melden Sie sich an und bestätigen Sie Ihre Bestellung.
    • Das Popup sollte sich schließen.
  • Überprüfen Sie, ob Sie auf eine Erfolgsseite weitergeleitet werden.
  • Überprüfen Sie abschließend, ob die Bestellung im PayPal-Konto durchgeführt wurde, indem Sie sich mit Ihrem Geschäftskonto unter https://www.sandbox.paypal.com/signin anmelden und das Dashboard https://www.sandbox.paypal.com/listing überprüfen /transaktionen.

PayPal-Abonnements

PayPal-Pläne/Verträge/Abonnements folgen derselben Logik wie Stripe-Abonnements und werden für wiederkehrende Zahlungen erstellt. Bei diesem Produkttyp wird dem Benutzer je nach Konfiguration täglich, wöchentlich, monatlich oder jährlich automatisch eine Rechnung gestellt.

Wir werden das Feld für das Produkt paypal_plan_name verwenden, um die von PayPal bereitgestellte Plan-ID zu speichern. In diesem Fall wählen wir im Gegensatz zu Stripe die ID nicht aus, und PayPal gibt diesen Wert zurück, der verwendet wird, um das zuletzt in unserer Datenbank erstellte Produkt zu aktualisieren.

Für die Erstellung eines Abonnements sind in keinem Schritt customer erforderlich, da die Methode onApprove diese Verknüpfung wahrscheinlich in ihrer zugrunde liegenden Implementierung behandelt. Unsere Tische bleiben also gleich.

Schritt 1: Erstellen Sie einen Plan mit der PayPal-API.

Öffnen Sie Ihre Konsole über die Befehlsleiste rails c . Erstellen Sie ein Abonnement für Ihr PayPal-Konto mit:

 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)

Schritt 2: Aktualisieren Sie das letzte Produkt in der Datenbank paypal_plan_name mit der zurückgegebenen plan.id .

Laufen:

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

Schritt 3: Fügen Sie Routen für das PayPal-Abonnement hinzu.

Fügen Sie zwei neue Routen 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

Schritt 4: Behandeln Sie die Erstellung und Ausführung im PayPal-Dienst.

Fügen Sie zwei weitere Funktionen zum Erstellen und Ausführen von Abonnements in Orders::Paypal von 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 initialisieren wir eine Vereinbarung, indem wir die Methode PayPal::SDK::REST::Agreement.new aufrufen und den product.paypal_plan_name als eines seiner Attribute übergeben. Danach erstellen wir es, und jetzt wird ein Token für dieses letzte Objekt gesetzt. Wir geben das Token auch an das Frontend zurück.

In execute_subscription finden wir den order , der im vorherigen Aufruf erstellt wurde. Danach initialisieren wir eine neue Vereinbarung, setzen das Token dieses vorherigen Objekts und führen es aus. Wenn dieser letzte Schritt erfolgreich durchgeführt wurde, wird der Bestellstatus auf paypal_executed gesetzt. Und jetzt geben wir die Vereinbarungs-ID an das Frontend zurück, die auch in order.chager_id gespeichert ist.

Schritt 5: Fügen Sie Aktionen zum Erstellen und Ausführen von Abonnements in orders_controller .

Ändern Sie die app/controllers/orders_controller.rb . Oben in der Klasse zuerst den Callback prepare_new_order aktualisieren und dann ausführen, bevor paypal_create_subscription aufgerufen wird:

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

Fügen Sie in derselben Datei auch die beiden öffentlichen Funktionen hinzu, damit sie den Dienst Orders::Paypal mit einem ähnlichen Ablauf aufrufen, wie wir ihn bereits bei PayPal-Zahlungen haben:

 ... 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 ...

Schritt 6: Hinzufügen von Abonnement-Handlern für createOrder und onApprove Callbacks im Frontend.

Ersetzen Sie schließlich in index.html.haml die paypal.Buttons Funktion durch die folgende, die die beiden leeren else Elemente füllt, die wir zuvor hatten:

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

Die Erstellung und Ausführung für Abonnements hat eine ähnliche Logik wie für Zahlungen. Ein Unterschied besteht darin, dass beim Ausführen von Zahlungen die Daten aus der Callback-Funktion onApprove bereits eine Zahlungs charge_id haben, die die paymentID darstellt, um das Formular über submitOrderPaypal(data.paymentID) zu übermitteln. Bei Abonnements erhalten wir die charge_id erst, nachdem wir sie ausgeführt haben, indem wir einen POST auf paypal_execute_subscription_url , sodass wir submitOrderPaypal(executeData.id) .

Schritt 7: Testen Sie Ihre Anwendung.

  • Besuchen Sie die Indexseite.
  • Wählen Sie ein Abonnementprodukt und PayPal als Zahlungsmethode aus.
  • Klicken Sie auf die Schaltfläche „PayPal senden“.
  • Im PayPal-Popup:
    • Verwenden Sie die Anmeldeinformationen für das von Ihnen erstellte Käuferkonto.
    • Melden Sie sich an und bestätigen Sie Ihre Bestellung.
    • Das Popup sollte sich schließen.
  • Überprüfen Sie, ob Sie auf eine Erfolgsseite weitergeleitet werden.
  • Überprüfen Sie abschließend, ob die Bestellung im PayPal-Konto ausgeführt wurde, indem Sie sich mit Ihrem Geschäftskonto unter https://www.sandbox.paypal.com/signin anmelden und das Dashboard https://www.sandbox.paypal.com/listing/ überprüfen. Transaktionen.

Fazit

Nachdem Sie diesen Artikel gelesen haben, sollten Sie in der Lage sein, Zahlungen/Gebühren sowie Abonnementtransaktionen für PayPal und Stripe in Ihre Rails-Anwendung zu integrieren. Es gibt viele Punkte, die verbessert werden könnten, die ich in diesem Artikel der Kürze halber nicht hinzugefügt habe. Ich habe alles basierend auf einer Annahme der Schwierigkeit organisiert:

  • Einfacher:
    • Verwenden Sie Transport Layer Security (TLS), damit Ihre Anfragen HTTPS verwenden.
    • Implementieren Sie Konfigurationen der Produktionsumgebung für PayPal und Stripe.
    • Fügen Sie eine neue Seite hinzu, damit Benutzer auf einen Verlauf früherer Bestellungen zugreifen können.
  • Mittel:
    • Abonnements zurückerstatten oder kündigen.
    • Bereitstellung einer Lösung für nicht registrierte Benutzerzahlungen.
  • Schwerer:
    • Bieten Sie eine Möglichkeit zum Entfernen von Konten und zum Beibehalten ihres Tokens und ihrer Kunden -ID, wenn der Benutzer zurückkehren möchte. Entfernen Sie diese Daten jedoch nach einer bestimmten Anzahl von Tagen, damit Ihre Anwendung PCI-konformer ist.
    • Wechseln Sie auf der Serverseite zur PayPal Version 2 API (https://developer.paypal.com/docs/api/payments/v2/). 2, sodass diese mit Vorsicht verwendet werden kann (https://github.com/paypal/PayPal-Ruby-SDK/tree/2.0-beta).
    • Schließen Sie idempotente Anfragen ein.
      • Streifen: https://stripe.com/docs/api/idempotent_requests
      • PayPal: https://developer.paypal.com/docs/api-basics/#api-idempotency

Ich empfehle auch, über das Stripe Checkout-Element zu lesen, das eine weitere Möglichkeit darstellt, Stripe in das Frontend zu integrieren. Im Gegensatz zu Stripe Elements, das wir in diesem Tutorial verwendet haben, öffnet Stripe Checkout nach dem Klicken auf eine Schaltfläche (ähnlich wie bei PayPal) ein Popup, in dem der Benutzer Kreditkarteninformationen eingibt ODER mit Google Pay/Apple Pay https://stripe.com bezahlen möchte /docs/web.

Eine zweite Leseempfehlung sind die Sicherheitsseiten für beide Payment Gateways.

  • Für Streifen
  • Für Paypal

Abschließend danke, dass Sie diesen Artikel gelesen haben! Sie können auch mein GitHub-Projekt überprüfen, das für dieses Projektbeispiel verwendet wird. Dort habe ich während der Entwicklung auch rspec- Tests hinzugefügt.