En nøkkelfunksjon for store e-handelsselskaper som AliExpress, Ebay og Amazon er en sikker måte å håndtere betalinger på, noe som er viktig for deres virksomhet. Hvis denne funksjonen mislykkes, vil konsekvensene være ødeleggende. Dette gjelder bransjeledere og Ruby on Rails-utviklere jobber med e-handelsapps.
Cybersikkerhet er viktig for å forhindre angrep, og en måte å gjøre transaksjonsprosessen sikrere er å be en tredjepartstjeneste om å håndtere den. Inkludering av betalingsportaler i applikasjonen din er en måte å oppnå dette målet på, da de gir brukerautorisasjon, datakryptering og et dashbord slik at du kan følge transaksjonsstatus på farten.
Det finnes en rekke betalingstjenester på nettet, men i denne artikkelen vil jeg fokusere på å integrere Stripe og PayPal til et Rails-program. For å nevne noen få andre: Amazon Payments, Square, SecurePay, WorldPay, Authorize.Net, 2Checkout.com, Braintree, Amazon eller BlueSnap.
Generelt vil det være et skjema / knapp i applikasjonen din der brukeren kan logge inn / sette inn kredittkortdata. PayPal og Stripe gjør allerede dette første trinnet sikrere ved å bruke iframe
skjemaer eller popups
som forhindrer søknaden din fra å lagre sensitiv brukerkredittkortinformasjon, da de vil returnere et token som representerer denne transaksjonen. Noen brukere kan også allerede føle seg tryggere på å behandle betalinger ved å vite at en tredjepartstjeneste håndterer transaksjonsprosessen, så dette kan også være en attraksjon for søknaden din.
Etter autentisering av brukerinformasjonen, vil en betalingsgateway bekrefte betalingen ved å kontakte en betalingsbehandler som kommuniserer med bankene for å gjøre opp betalinger. Dette sikrer at transaksjonen belastes / godskrives ordentlig.
Stripe bruker et kredittkortskjema som ber om kredittkortnummer, CV og utløpsdato. Så brukeren må fylle ut kredittkortinformasjon i de sikrede Stripe-inngangene. Etter at du har gitt denne informasjonen, behandler søknadsforsvaret denne betalingen gjennom et token.
I motsetning til Stripe, viderekobler PayPal brukeren til PayPal-påloggingssiden. Brukeren autoriserer og velger betalingsmåte via PayPal, og igjen vil din bakre håndtere poletter i stedet for brukersensitive data.
Det er viktig å nevne at for disse to betalingsportalene, bør bakenden din be om å fortsette transaksjonskjøringen gjennom Stripe eller PayPal API-er, noe som vil gi et OK / NOK-svar, så søknaden din bør omdirigere brukeren til en vellykket eller feilside tilsvarende.
Hensikten med denne artikkelen er å gi en rask guide for å integrere disse to betalingsportalene i en enkelt applikasjon. For alle tester vil vi bruke sandkasser og testkontoer levert av Stripe og PayPal for å simulere betalinger.
Før vi integrerer betalingsportaler, vil vi gjøre et oppsett for å initialisere applikasjonen ved å legge til perler, databasetabeller og en indeksside. Dette prosjektet ble opprettet ved hjelp av Rails versjon 5.2.3 og Ruby 2.6.3.
Merk: Du kan sjekke ut nytt Rails 6 har i vår siste artikkel .
Trinn 1: Initialiser et Rails-program.
Initialiser prosjektet ved å kjøre prosjektinitialiseringen med rails
kommando med appnavnet ditt:
rails new YOUR_APP_NAME
Og cd
i applikasjonsmappen din.
Steg 2: Installer perler.
Foruten Stripe og PayPal perler, ble det lagt til noen få andre perler:
devise
: brukes til brukerautentisering og autorisasjonhaml
: malverktøy for gjengivelse av brukersiderjquery-rails
: for jquery
i front-end-manusenemoney-rails
: for visning av formaterte pengeverdierLegg til Gemfile
:
gem 'devise', '>= 4.7.1' gem 'haml' gem 'jquery-rails' gem 'money-rails'
Etter at du har lagt den til, kjør i CLI:
bundle install
Trinn 3: Initialiser edelstener.
Noen av disse perlene vil kreve initialisering i tillegg til å installere dem gjennom bundle
.
Installere utarbeide:
rails g devise:install
Initialiserer money-rails
:
rails g money_rails:initializer
Initialiser jquery-rails
ved å legge til bunnen av app/assets/javascripts/application.js
følgende:
//= require jquery //= require jquery_ujs
Trinn 4: Tabeller og migrasjoner
Tre bord vil bli brukt i dette prosjektet Brukere , Produkter , og Bestillinger .
Users
: Vil bli generert gjennom utformingProducts
kolonner:name
price_cents
Stripe_plan_name
: En ID som representerer en abonnementsplan opprettet i Stripe, slik at brukerne kan abonnere på den. Dette feltet er bare obligatorisk for produkter som er tilknyttet en Stripe-plan.paypal_plan_name
: Det samme som stripe_plan_name
men for PayPalOrders
kolonner:product_id
user_id
status
: Dette vil informere om bestillingen venter, mislykkes eller betales.token
: Dette er et token generert fra API-ene (enten Stripe eller PayPal) for å initialisere en transaksjon.price_cents
: Ligner på produktet, men brukes for å gjøre denne verdien vedvarende i ordrepostenpayment_gateway
: Lagrer hvilken betalingsgateway som brukes for bestillingen PayPal eller Stripecustomer_id
: Dette vil bli brukt til Stripe for å lagre Stripe-kunden for et abonnement, og det vil bli forklart mer detaljert i et senere avsnitt.For å generere disse tabellene, må det genereres noen migreringer:
For å skape Brukertabell . Løpe:
rails g devise User
For å skape Produkter tabell . Generer en migrering ved å kjøre:
rails generate migration CreateProducts name:string stripe_plan_name:string paypal_plan_name:string
Åpne den opprettede migreringsfilen din, som skal være plassert på db/migrate/
, og gjør endringer for at migrasjonen din skal ligne på denne:
class CreateProducts For å skape Bestillingsbord . Generer en migrering ved å kjøre:
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
Åpne igjen den opprettede migreringsfilen din som skal være på db/migrate/
og gjør endringer i den filen for å få den til å ligne på denne:
class CreateOrders Kjør databasemigrasjoner ved å utføre:
rails db:migrate
Trinn 5: Lag modeller.
Brukermodellen er allerede opprettet fra planlegge installasjon, og det vil ikke være behov for endringer på den. I tillegg til det vil det bli laget to modeller for Produkt og Rekkefølge .
Produkt. Legg til en ny fil, app/models/product.rb
, med:
class Product Rekkefølge. Legg til en ny fil, app/models/order.rb
, med:
class Order { 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
Trinn 6: Befolk databasen.
En bruker og to produkter vil bli opprettet i konsollen. Ordreoppføringer vil bli opprettet i henhold til betalingstester.
- Kjør
rails s
- Gå til
http://localhost:3000
i nettleseren din - Du blir omdirigert til en registreringsside.
- Registrer en bruker ved å fylle ut e-postadressen og passordet.
- I terminalen din blir følgende logger bedt om å vise at en bruker ble opprettet i databasen din:
User Create (0.1ms) INSERT INTO 'users' ('email', 'encrypted_password', 'created_at', 'updated_at') VALUES (?, ?, ?, ?) …
- Opprett to produkter uten abonnementer ved å kjøre
rails c
og legger til: Product.create(name: 'Awesome T-Shirt', price_cents: 3000)
Product.create(name: 'Awesome Sneakers', price_cents: 5000)
Trinn 7: Opprett en indeksside
Hovedsiden for prosjektet inkluderer produktvalg for kjøp eller abonnement. I tillegg har den også en seksjon for valg av betalingsmåte (Stripe eller PayPal). En send-knapp brukes også for hver betalingstypetype, for PayPal vil vi legge til sin egen knappedesign gjennom JavaScript-biblioteket.
Først oppretter du rutene for index
og submit
i config/routes.rb
.
Rails.application.routes.draw do devise_for :users get '/', to: 'orders#index' post '/orders/submit', to: 'orders#submit' end
Opprett og legg til handlinger index
og submit
i ordrekontrolløren app/controllers/orders_controller.rb
. orders#index
action lagrer to variabler som skal konsumeres i front-enden: @products_purchase
som har en liste over produkter uten planer og @products_subscription
som har produkter med både PayPal- og Stripe-planer.
class OrdersController Opprett en fil i app/views/orders/index.html.haml
. Denne filen inneholder alle inngangene vi skal sende til vår bakside gjennom innsendingsmetoden, og samspillet for betalingsportaler og produktvalg. Her er noen inngangsnavnattributter:
Orders[product_id]
lagrer produkt-ID. Orders[payment_gateway]
inneholder betalingsportalen med enten Stripe- eller PayPal-verdier for den andre.
%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; }
Hvis du kjører søknaden din med rails s
og besøk siden din i http://localhost:3000
. Du bør kunne se siden som følger:
Rå indeks side uten Stripe og PayPal integrasjon Lager for legitimasjon for betalingsgateway
PayPal og Stripe-nøkler lagres i en fil som ikke spores av Git. Det er to typer nøkler som er lagret i denne filen for hver betalingsportal, og foreløpig bruker vi en dummy-verdi for dem. Ytterligere anvisninger for å lage disse nøklene er presentert i ytterligere seksjoner.
Trinn 1: Legg til dette i .gitignore
.
/config/application.yml
Steg 2: Opprett en fil med legitimasjonen din i config/application.yml
. Den skal inneholde alle PayPal- og Stripe-sandkasse- / testnøkler for å få tilgang til disse API-ene.
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
Trinn 3: For å lagre variablene fra filen config/application.yml
når applikasjonen starter, legg til disse linjene i config/application.rb
inne i Application
klasse slik at de vil være tilgjengelige i ENV
.
config_file = Rails.application.config_for(:application) config_file.each do |key,value| ENV[key] = value end unless config_file.nil?
Stripe-konfigurasjon
Vi vil legge til en perle for bruk av Stripe API: stripe-rails
. Det er også nødvendig å opprette en Stripe-konto slik at gebyrer og abonnementer kan behandles. Hvis du må, kan du konsultere API-metodene for Stripe API i offisiell dokumentasjon .
Trinn 1: Legg stripe-perlen til prosjektet ditt.
De stripeskinner perle vil gi et grensesnitt for alle API-forespørsler som brukes i dette prosjektet.
Legg til dette i Gemfile
:
gem 'stripe-rails'
Løpe:
bundle install
Steg 2: Generer API-nøklene dine.
For å ha API-nøklene for å kommunisere med Stripe, må du opprette en konto i Stripe. For å teste applikasjonen er det mulig å bruke testmodus, så ingen reell forretningsinformasjon må fylles ut i løpet av opprettelsen av Stripe-konto.
- Opprett en konto i Stripe hvis du ikke har en ( https://dashboard.stripe.com/ ).
- Mens du fortsatt er på Stripe-dashbordet, bytter du etter å ha logget på Se testdata på.
- På https://dashboard.stripe.com/test/apikeys , erstatt
YOUR_CREDENTIAL_HERE
for verdiene STRIPE_PUBLISHABLE_KEY
og STRIPE_SECRET_KEY
i /config/application.yml
med innholdet fra Publishable Key
og Secret key
.
Trinn 3: Initialiser Stripe-modulen
I tillegg til å erstatte nøklene, trenger vi fortsatt å initialisere Stripe-modulen, slik at den bruker nøklene som allerede er angitt i ENV
.
Opprett en fil i config/initializers/stripe.rb
med:
Rails.application.configure do config.stripe.secret_key = ENV['STRIPE_SECRET_KEY'] config.stripe.publishable_key = ENV['STRIPE_PUBLISHABLE_KEY'] end
Trinn 4: Integrer Stripe i frontenden.
Vi vil legge til Stripe JavaScript-biblioteket og logikken for å sende et token som representerer brukerens kredittkortinformasjon og vil bli behandlet i vår bakside.
I index.html.haml
fil, legg dette til øverst i filen. Dette vil bruke Stripe-modulen (levert av perlen) for å legge til Stripe javascript-biblioteket til brukerens side.
= stripe_javascript_tag
Stripe bruker sikre inndatafelt som er opprettet gjennom API-en. Når de er opprettet i en iframe
opprettet gjennom dette API-et, trenger du ikke å bekymre deg for mulige sårbarheter som håndterer brukerens kredittkortinformasjon. I tillegg vil ikke bakenden din kunne behandle / lagre brukerfølsomme data, og den vil bare motta et token som representerer denne informasjonen.
Disse inntastingsfeltene opprettes ved å ringe stripe.elements().create('card')
. Etter det er det bare nødvendig å ringe det returnerte objektet med mount()
ved å sende som argument HTML-element-id / klasse der disse inngangene skal monteres. Mer informasjon finner du på Stripe .
Når brukeren trykker på sendeknappen med Stripe-betalingsmetoden, utføres en annen API-samtale som returnerer et løfte på det opprettede Stripe-kortelementet:
stripe.createToken(card).then(function(result)
result
variabelen til denne funksjonen, hvis den ikke har tildelt en eiendomsfeil, vil ha et token som kan hentes ved å få tilgang til attributtet result.token.id
. Dette token vil bli sendt til bakenden.
For å gjøre disse endringene, erstatt den kommenterte koden // YOUR STRIPE AND PAYPAL CODE WILL BE HERE
i index.html.haml
med:
(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($('').val(result.token.id)); // Set order type $('#order-type').val('stripe'); $form.submit(); } }); return false; }); }()); //YOUR PAYPAL CODE WILL BE HERE
Hvis du besøker siden din, bør det se ut som følger med de nye Stripe sikre inndatafeltene:
Indekside integrert med Stripesikre inndatafelt. Trinn 5: Test søknaden din.
Fyll ut kredittkortskjemaet med et testkort ( https://stripe.com/docs/testing ) og send inn siden. Sjekk om submit
handling kalles med alle parametere ( Produkt ID , betaling_gateway , og token ) i serverutgangen.
Stripe Charges
Stripgebyr representerer engangstransaksjoner. Derfor vil du motta penger direkte fra klienten etter en Stripe-belastningstransaksjon. Dette er ideelt for salg av produkter som ikke er knyttet til planer. I en senere del vil jeg vise hvordan du gjør samme transaksjonstype med PayPal, men PayPal har navnet for denne typen transaksjoner innbetaling .
I denne delen vil jeg også gi alt skjelettet for å håndtere og sende inn en ordre. Vi oppretter en ordre i submit
handling når Stripe-skjemaet sendes inn. Denne ordren vil i utgangspunktet ha Avventer status, så hvis noe går galt mens du behandler denne bestillingen, vil bestillingen fortsatt være Avventer .
Hvis det oppstår feil fra Stripe API-anrop, setter vi rekkefølgen i a mislyktes tilstand, og hvis belastningen er fullført, vil den være i betalt stat. Brukeren blir også omdirigert i henhold til Stripe API-svaret som vist i følgende graf:
Stripetransaksjoner. I tillegg, når en stripe-belastning utføres, returneres en ID. Vi lagrer denne ID-en slik at du senere kan se etter den i Stripe-dashbordet ditt hvis nødvendig. Denne ID-en kan også brukes hvis ordren må refunderes. En slik ting vil ikke bli utforsket i denne artikkelen.
Trinn 1: Opprett Stripe-tjenesten.
Vi bruker en singleton-klasse for å representere Stripe-operasjoner ved hjelp av Stripe API. For å lage en kostnad, er metoden Stripe::Charge.create
kalles, og det returnerte objekt-ID-attributtet lagres i ordreposten charge_id
. Dette create
funksjon kalles ved å sende tokenet som stammer fra frontend, bestillingsprisen og en beskrivelse.
Så opprett en ny mappe app/services/orders
, og legg til en stripetjeneste: app/services/orders/stripe.rb
inneholder Orders::Stripe
singleton-klasse, som har en oppføring i metoden 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
Steg 2: Gjennomfør innsendingshandlingen og ring Stripe-tjenesten.
I orders_controller.rb
, legg til følgende i submit
handling, som i utgangspunktet vil kalle tjenesten Orders::Stripe.execute
. Merk at to nye private funksjoner også ble lagt til: prepare_new_order
og 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
Trinn 3: Test søknaden din.
Sjekk om innsendingshandlingen, når den ringes med et gyldig testkort, utfører en viderekobling til en vellykket melding. I tillegg sjekker du inn Stripe dashbord hvis bestillingen også vises.
Stripe-abonnementer
Abonnementer eller planer kan opprettes for gjentatte betalinger. Med denne typen produkter belastes brukeren daglig, ukentlig, månedlig eller årlig i henhold til plankonfigurasjon . I denne delen vil vi bruke feltet for produkt stripe_plan_name
for å lagre plan-ID - faktisk er det mulig for oss å velge ID, og vi vil kalle det premium-plan
—som vil bli brukt for å opprette forholdet customer subscription
.
Vi vil også opprette en ny kolonne for brukertabellen kalt stripe_customer_id
som fylles med id-egenskapen til et Stripe-kundeobjekt. En Stripe-kunde opprettes når funksjonen Stripe::Customer.create
kalles, og du kan også sjekke kundene som er opprettet og koblet til kontoen din i ( https://dashboard.stripe.com/test/customers ). Kunder opprettes ved å sende et source
parameter som i vårt tilfelle er tokenet som genereres i frontenden som sendes når skjemaet sendes inn.
Kundeobjektet hentet fra sistnevnte Stripe API-anrop, brukes også til å opprette et abonnement som gjøres ved å ringe customer.subscriptions.create
og sende plan-ID som parameter.
I tillegg vil stripe-rails
gem gir grensesnittet for å hente og oppdatere en kunde fra Stripe, som gjøres ved å ringe Stripe::Customer.retrieve
og henholdsvis Stripe::Customer.update
Så når en brukerpost allerede har en stripe_customer_id
, i stedet for å opprette en ny kunde ved hjelp av Stripe::Customer.create
, vil vi ringe Stripe::Customer.retrieve
passerer stripe_customer_id
som parameter, etterfulgt av a Stripe::Customer.update
, og i dette tilfellet å sende token en parameter.
For det første skal vi lage en plan ved hjelp av Stripe API, slik at vi kan lage et nytt abonnementsprodukt ved hjelp av feltet stripe_plan_name
. Etterpå vil vi gjøre endringer i orders_controller
og Stripe-tjeneste slik at opprettelse og gjennomføring av Stripe-abonnementer håndteres.
Trinn 1: Lag en plan ved hjelp av Stripe API.
Åpne konsollen ved hjelp av kommandoen rails c
. Opprett abonnement for Stripe-kontoen din med:
Stripe::Plan.create({ amount: 10000, interval: 'month', product: { name: 'Premium plan', }, currency: 'usd', id: 'premium-plan', })
Hvis det returnerte resultatet i dette trinnet er sant, betyr det at planen ble opprettet, og du kan få tilgang til den i din Stripe dashbord .
Steg 2: Opprett et produkt i databasen med stripe_plan_name
feltsett.
Lag nå et produkt med stripe_plan_name
angitt som premium-plan
i databasen:
Product.create(price_cents: 10000, name: 'Premium Plan', stripe_plan_name: 'premium-plan')
Trinn 3: Generer en migrering for å legge til en kolonne stripe_customer_id
i users
bord.
Kjør følgende i terminalen:
rails generate migration AddStripeCustomerIdToUser stripe_customer_id:string rails db:migrate
Trinn 4: Implementer abonnementslogikken i Stripe-serviceklassen.
Legg til to funksjoner til i de private metodene til app/services/orders/stripe.rb
: execute_subscription
er ansvarlig for å lage abonnementene i kundens objekt. Funksjonen find_or_create_customer
er ansvarlig for å returnere den allerede opprettede kunden eller ved å returnere en nyopprettet kunde.
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
Til slutt, i execute
funksjon i samme fil (app/services/orders/stripe.rb
), vil vi først ringe find_or_create_customer
og deretter utføre abonnementet ved å ringe execute_subscription
ved å passere den forrige hentede / opprettede kunden. Så erstatt kommentaren #SUBSCRIPTIONS WILL BE HANDLED HERE
i execute
metode med følgende kode:
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)
Trinn 5: Test søknaden din.
Besøk nettstedet ditt, velg abonnementsproduktet Premium Plan
, og fyll ut et gyldig testkort. Etter at du har sendt den, skal den omdirigere deg til en vellykket side. I tillegg sjekker du inn Stripe dashbord hvis abonnementet ble opprettet.
PayPal-konfigurasjon
Som vi gjorde i Stripe, vil vi også legge til en perle for bruk av PayPal API: paypal-sdk-rest
, og det er også nødvendig å opprette en PayPal-konto. En beskrivende arbeidsflyt for PayPal som bruker denne perlen, kan konsulteres i tjenestemannen PayPal API-dokumentasjon .
Trinn 1: Legg til paypal-sdk-rest
perle til prosjektet ditt.
Legg til dette i Gemfile
:
gem 'paypal-sdk-rest'
Løpe:
bundle install
Steg 2: Generer API-nøklene dine.
For å ha API-nøklene for å kommunisere med PayPal, må du opprette en PayPal-konto. Så:
- Opprett en konto (eller bruk PayPal-kontoen din) på https://developer.paypal.com/ .
- Fortsatt logget på kontoen din, opprett to sandkassokontoer på https://developer.paypal.com/developer/accounts/ :
- Personlig (kjøperkonto) - Dette vil bli brukt i testene dine for å foreta betalinger og abonnementer.
- Business (Merchant Account) - Dette vil bli koblet til applikasjonen, som vil ha API-nøklene vi leter etter. I tillegg til det kan alle transaksjoner følges i denne kontoen.
- Opprett en app på https://developer.paypal.com/developer/applications bruker den forrige forretningssandkaskontoen.
- Etter dette trinnet vil du motta to nøkler for PayPal:
Client ID
og Secret
. - I
config/application.yml
, erstatt YOUR_CREDENTIAL_HERE
fra PAYPAL_CLIENT_ID
og PAYPAL_CLIENT_SECRET
med nøklene du nettopp har mottatt.
Trinn 3: Initialiser PayPal-modulen.
I likhet med Stripe, foruten å erstatte nøklene i application.yml
, må vi fortsatt initialisere PayPal-modulen slik at den kan bruke nøklene som allerede er angitt i ENV
variabel. For dette formålet, opprett en fil i config/initializers/paypal.rb
med:
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
Trinn 4: Integrer PayPal i frontenden.
I index.html.haml
legg dette til toppen av filen:
%script(src='https://www.paypal.com/sdk/js?client-id=#{ENV['PAYPAL_CLIENT_ID']}')
I motsetning til Stripe bruker PayPal bare en knapp som, når du klikker på den, åpner en sikker popup der brukeren kan logge på og fortsette med betaling / abonnement. Denne knappen kan gjengis ved å ringe metoden paypal.Button(PARAM1).render(PARAM2)
.
PARAM1
er et objekt med miljøkonfigurasjonen og to tilbakeringingsfunksjoner som egenskaper: createOrder
og onApprove
. PARAM2
angir HTML-elementidentifikatoren som PayPal-knappen skal festes til.
Så, fortsatt i samme fil, erstatt den kommenterte koden YOUR PAYPAL CODE WILL BE HERE
med:
(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($('').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'); }());
Trinn 5: Test søknaden din.
Besøk siden din og sjekk om PayPal-knappen blir gjengitt når du velger PayPal som betalingsmåte.
PayPal-transaksjoner
Logikken for PayPal-transaksjoner, i motsetning til Stripe, er litt mer kompleks ved å involvere flere forespørsler fra frontenden til bakenden. Derfor eksisterer denne delen. Jeg vil forklare mer eller mindre (uten kode) hvordan funksjonene beskrevet i createOrder
og onApprove
metoder skal implementeres og hva som forventes i backend-prosessene også.
Trinn 1: Når brukeren klikker på PayPal-sendeknappen, er en PayPal-popup som ber om brukerlegitimasjon åpen, men i lastetilstand. Funksjonen tilbakeringing createOrder
er kalt.
PayPal popup, lastestatus Steg 2: I denne funksjonen vil vi utføre en forespørsel til back-enden som vil opprette en betaling / abonnement. Dette er begynnelsen på en transaksjon, og ingen gebyrer vil bli påført ennå, så transaksjonen er faktisk i en Avventer stat. Vår bakside skal gi oss et token som genereres ved hjelp av PayPal-modul (levert gjennom paypal-rest-sdk
perlen).
Trinn 3: Fortsatt i createOrder
tilbakeringing, vi returnerer dette tokenet som er generert i back-enden, og hvis alt er i orden, vil PayPal-popup-vinduet gjengi følgende og be om brukerlegitimasjon:
PayPal popup, brukerlegitimasjon Trinn 4: Etter at brukeren har logget på og valgt betalingsmåte, vil popup-en endre statusen til følgende:
PayPal popup, autorisert transaksjon Trinn 5: onApprove
funksjon tilbakeringing kalles nå. Vi har definert det som følgende: onApprove: function(data)
. data
objektet vil ha betalingsinformasjonen for å kunne utføre den. I denne tilbakeringingen vil en annen forespørsel til back-end-funksjonen bli utført denne gangen og sende dataobjektet for å utføre PayPal-bestillingen.
Trinn 6: Vår bakside utfører denne transaksjonen og returnerer 200 (hvis vellykket).
Trinn 7: Når bakenden kommer tilbake, sender vi inn skjemaet. Dette er den tredje forespørselen vi stiller til vår bakside.
Vær oppmerksom på at, i motsetning til Stripe, er det tre forespørsler om back-enden vår i denne prosessen. Og vi vil holde status for ordreoppføringen synkronisert tilsvarende:
createOrder
tilbakeringing: Det opprettes en transaksjon, og det opprettes også en ordreoppføring; derfor er det i en Avventer status som standard. onApprove
tilbakeringing: Transaksjonen blir utført og bestillingen vår blir satt til paypal_utført . - Bestillingssiden er sendt: Transaksjonen ble allerede utført, så ingenting endres. Bestillingsposten endrer status til betalt .
Hele denne prosessen er beskrevet i følgende graf:
PayPal-transaksjoner PayPal-betalinger
PayPal-betalinger følger samme logikk som Stripe Charges, så de representerer engangstransaksjoner, men som nevnt i forrige avsnitt har de en annen flytlogikk. Dette er endringene som må utføres for å håndtere PayPal-betalinger:
Trinn 1: Lag nye ruter for PayPal og utfør betalinger.
Legg til følgende ruter i 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
Dette vil opprette to nye ruter for å opprette og utføre betalinger som vil bli håndtert i paypal_create_payment
og paypal_execute_payment
bestiller kontrollermetoder.
Steg 2: Opprett PayPal-tjenesten.
Legg til singleton-klassen Orders::Paypal
på: app/services/orders/paypal.rb
.
Denne tjenesten vil i utgangspunktet ha tre ansvarsområder:
create_payment
metoden oppretter en betaling ved å ringe PayPal::SDK::REST::Payment.new
. EN token genereres og returneres til frontend. execute_payment
metoden utfører betalingen ved først å finne det forrige opprettede betalingsobjektet gjennom PayPal::SDK::REST::Payment.find(payment_id)
som bruker betaling_id som et argument som har samme verdi som charge_id lagret i forrige trinn i ordreobjektet. Etter det ringer vi execute
i betalingsobjektet med en gitt betaler som parameter. Denne betaleren gis av grensesnittet etter at brukeren har oppgitt legitimasjon og valgt en betalingsmåte i popup-vinduet. finish
metoden finner en ordre etter en bestemt charge_id spørring etter nylig opprettede bestillinger i paypal_utført stat. Hvis en post blir funnet, blir den merket som betalt.
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
Trinn 3: Ring PayPal-tjenesten i kontrolleren i innsendingshandlingen.
Legg til en tilbakeringing for prepare_new_order
før handlingen paypal_create_payment
(som vil bli lagt til i neste trinn) blir bedt om ved å legge til følgende i filen app/controllers/orders_controller.rb
:
class OrdersController Igjen, i samme fil, ring PayPal-tjenesten i innsendingshandlingen ved å erstatte den kommenterte koden #PAYPAL WILL BE HANDLED HERE.
med følgende:
... elsif order_params[:payment_gateway] == 'paypal' @order = Orders::Paypal.finish(order_params[:token]) end ...
Trinn 4: Lag handlingene for håndtering av forespørsler.
Likevel, i app/controllers/orders_controller.rb
fil, opprett to nye handlinger (som skal være offentlige) for håndtering av forespørsler til paypal_create_payment
og paypal_execute_payment
ruter:
paypal_create_payment
metode: Kaller tjenestemetoden vår create_payment
. Hvis det returnerer vellykket, vil det returnere bestillingen token opprettet av Orders::Paypal.create_payment
. paypal_execute_payment
metode: Kaller tjenestemetoden vår execute_payment
(som utfører betalingene våre). Hvis betalingen er vellykket, returnerer den 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 ...
Trinn 5: Implementere front-end tilbakeringingsfunksjonene for createOrder
og onApprove
.
Lag din paypal.Button.render
ring ser slik ut:
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');
Som nevnt i forrige avsnitt kaller vi paypal_create_payment_url
for createOrder
tilbakeringing og paypal_execute_payment_url
for onApprove
Ring tilbake. Legg merke til at hvis den siste forespørselen returnerer suksess, sender vi inn bestillingen, som er den tredje forespørselen som er sendt til serveren.
I createOrder
funksjonsbehandler, returnerer vi et token (hentet fra bakenden). I onApprove
tilbakeringing, vi har to egenskaper sendt til back-end paymentID
og payerID
. Disse vil bli brukt for å utføre betalingen.
Merk til slutt at vi har to tomme else
klausuler når jeg gir plass til neste avsnitt der vi skal legge til PayPal-abonnementer.
Hvis du besøker siden din etter å ha integrert front-end JavaScript-delen og velger PayPal som betalingsmåte, bør det se slik ut:
Indekside etter integrering med PayPal Trinn 6: Test søknaden din.
- Besøk indeksiden.
- Velg et betalings- / avgiftsprodukt og PayPal som betalingsmåte.
- Klikk på Send PayPal-knappen.
- I PayPal-popup:
- Bruk legitimasjonen til kjøperkontoen du opprettet.
- Logg inn og bekreft bestillingen.
- Popup-vinduet skal lukkes.
- Sjekk om du blir omdirigert til en suksessside.
- Til slutt, sjekk om bestillingen ble utført på PayPal-kontoen ved å logge på med bedriftskontoen din på https://www.sandbox.paypal.com/signin og sjekke dashbordet https://www.sandbox.paypal.com/listing/transactions .
PayPal-abonnementer
PayPal-planer / avtaler / abonnementer følger samme logikk som Stripe-abonnementer, og er opprettet for gjentatte betalinger. Med denne typen produkter belastes brukeren daglig, ukentlig, månedlig eller årlig i henhold til brukerens konfigurasjon .
Vi bruker feltet for produkt paypal_plan_name
for å lagre plan-ID levert av PayPal. I dette tilfellet, forskjellig fra Stripe, velger vi ikke ID-en, og PayPal returnerer denne verdien som vil bli brukt til å oppdatere det siste produktet som ble opprettet i databasen vår.
For å opprette et abonnement, ingen customer
informasjon kreves i ethvert trinn, som metoden onApprove
håndterer sannsynligvis denne koblingen i den underliggende implementeringen. Så bordene våre vil forbli de samme.
Trinn 1: Lag en plan ved hjelp av PayPal API.
Åpne konsollen ved hjelp av kommandoen rails c
. Opprett et abonnement for PayPal-kontoen din med:
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)
Steg 2: Oppdater det siste produktet i databasen paypal_plan_name
med den returnerte plan.id
.
Løpe:
Product.last.update(paypal_plan_name: plan.id)
Trinn 3: Legg til ruter for PayPal-abonnement.
Legg til to nye ruter i 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
Trinn 4: Håndter oppretting og utførelse i PayPal-tjenesten.
Legg til to funksjoner til for å opprette og utføre abonnementer i Orders::Paypal
av 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
I create_subscription
initialiserer vi en avtale ved å kalle metoden PayPal::SDK::REST::Agreement.new
og passerer product.paypal_plan_name
som en av egenskapene. Etterpå lager vi det, og nå settes et token for dette siste objektet. Vi returnerer også token til frontenden.
I execute_subscription
finner vi order
posten som ble opprettet i forrige samtale. Etter det initialiserer vi en ny avtale, vi setter tegnet på dette forrige objektet og utfører det. Hvis dette siste trinnet er vellykket, er ordrestatus satt til paypal_utført . Og nå går vi tilbake til frontend avtalen ID som også er lagret i order.chager_id
.
Trinn 5: Legg til handlinger for å opprette og utføre abonnementer i orders_controller
.
Endre app/controllers/orders_controller.rb
. Først i toppen av klassen, og oppdater deretter tilbakeringingen prepare_new_order
skal også utføres før paypal_create_subscription
er kalt:
class OrdersController Legg også til de to offentlige funksjonene i samme fil, slik at de kaller Orders::Paypal
tjeneste med en lignende flyt som vi allerede har i PayPal-betalinger:
... 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 ...
Trinn 6: Legger til abonnementshåndtering for createOrder
og onApprove
tilbakeringinger i frontenden.
Endelig, i index.html.haml
, erstatt paypal.Buttons
funksjon med følgende, som vil fylle de to tomme else
vi hadde før:
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');
Opprettelse og utførelse for abonnement har en lignende logikk som brukt for betalinger. En forskjell er at dataene fra tilbakeringingsfunksjonen onApprove
når du utfører betalinger har allerede en paymentID
som representerer charge_id
å sende skjemaet gjennom submitOrderPaypal(data.paymentID)
. For abonnementer får vi charge_id
bare etter å ha utført den ved å be om en POST
på paypal_execute_subscription_url
, så vi kan ringe submitOrderPaypal(executeData.id)
.
Trinn 7: Test søknaden din.
- Besøk indeksiden.
- Velg et abonnementsprodukt og PayPal som betalingsmåte.
- Klikk på Send PayPal-knappen.
- I PayPal-popup:
- Bruk legitimasjonen til kjøperkontoen du opprettet.
- Logg inn og bekreft bestillingen.
- Popup-vinduet skal lukkes.
- Sjekk om du blir omdirigert til en suksessside.
- Kontroller til slutt om bestillingen ble utført på PayPal-kontoen ved å logge på med bedriftskontoen din på https://www.sandbox.paypal.com/signin og sjekke dashbordet https://www.sandbox.paypal.com/listing/transactions .
Konklusjon
Etter å ha lest denne artikkelen, bør du kunne integrere betalinger / avgifter samt abonnementstransaksjoner for PayPal og Stripe i Rails-applikasjonen. Det er mange punkter som kan forbedres, som jeg ikke la til i denne artikkelen for kortfattethet. Jeg organiserte alt basert på en antagelse om vanskeligheter:
- Lettere:
- Bruk Transport Layer Security (TLS) slik at dine forespørsler bruker HTTPS.
- Implementere produksjonsmiljøkonfigurasjoner for både PayPal og Stripe.
- Legg til en ny side slik at brukere kan få tilgang til en historie med tidligere ordrer.
- Medium:
- Refunder eller kanseller abonnement.
- Gi en løsning for ikke-registrerte brukerbetalinger.
- Hardere:
- Gi en måte å fjerne kontoer og beholde token og Kunde ID hvis brukeren ønsker å komme tilbake. Men etter en viss mengde dager, fjern disse dataene slik at applikasjonen din er mer PCI-kompatibel.
- Gå til PayPal versjon 2 API på serversiden ( https://developer.paypal.com/docs/api/payments/v2/ ) Perlen vi brukte i denne opplæringen paypal-sdk-hvile , har bare en betaversjon for versjon 2, slik at den kan brukes med forsiktighet ( https://github.com/paypal/PayPal-Ruby-SDK/tree/2.0-beta ).
- Inkluder idempotente forespørsler.
Jeg anbefaler også å lese om Stripe Checkout-element, som er en annen måte å integrere Stripe i frontenden. I motsetning til Stripe Elements, som vi brukte i denne opplæringen, åpner Stripe Checkout en popup etter å ha klikket på en knapp (ligner på PayPal) der brukeren fyller ut kredittkortinformasjon ELLER velger å betale med Google Pay / Apple Pay https://stripe.com/docs/web .
En annen leseanbefaling er sikkerhetssidene for begge Payment Gateways.
Til slutt, takk for at du leser denne artikkelen! Du kan også sjekke min GitHub-prosjekt brukt til dette prosjekteksemplet . Der la jeg til rspec tester også mens du utvikler.
Forstå det grunnleggende
Hva er Stripe og hvordan fungerer det?
Stripe er et selskap som utvikler programvare for enkeltpersoner eller virksomheter for å utføre sikre betalinger over internett.
Hva er forskjellen mellom PayPal og Stripe?
De tilbyr forskjellige applikasjoner som involverer betaling, og de har forskjellige gebyrer for å bruke tjenestene sine.
Hva er et betalingsmåte-token?
Betalingstokenisering er en prosess for å håndtere sensitive data fra brukere og transformere dem til tokens, så det er ingen sensitiv datalekkasje.
Er PayPal en betalingsportal eller prosessor?
PayPal er ikke en gateway, men en komplett selgerløsning. Imidlertid bruker den en betalingsgateway kalt Payflow.
Er Stripe en betalingsgateway eller prosessor?
Stripe er en betalingsport som håndterer kundenes kort.