#289 PayPal Recurring Billing pro
Oct 10, 2011 | 24 minutes | eCommerce
Here I show how to add PayPal recurring payments to an existing checkout process using PayPal's Express Checkout and the paypal-recurring gem.
- Download:
- source codeProject Files in Zip (137 KB)
- mp4Full Size H.264 Video (60.2 MB)
- m4vSmaller H.264 Video (30 MB)
- webmFull Size VP8 Video (34 MB)
- ogvFull Size Theora Video (73.9 MB)
Resources
- Episode 141: PayPal Basics
- PayPal Developer Sandbox
- PayPal Recurring Payments Comparison
- PayPal Name-Value Pair API (PDF)
- PayPal Recurring Gem
- PayPal Recurring Gem Request Options
Gemfile
gem 'paypal-recurring'
bash
rails g migration add_paypal_to_subscriptions paypal_customer_token:string paypal_recurring_profile_token:string rake db:migrate
config/initializers/paypal.rb
PayPal::Recurring.configure do |config| config.sandbox = true config.username = "seller_1318199877_biz_api1.railscasts.com" config.password = "1318199913" config.signature = "AiPC9BjkCyDFQXbSkoZcgqH3hpacA8A8VR5V3XCWZrbuhAAnOSWnbvGl" end
routes.rb
get 'paypal/checkout', to: 'subscriptions#paypal_checkout'
subscriptions_controller.rb
def new plan = Plan.find(params[:plan_id]) @subscription = plan.subscriptions.build if params[:PayerID] @subscription.paypal_customer_token = params[:PayerID] @subscription.paypal_payment_token = params[:token] @subscription.email = @subscription.paypal.checkout_details.email end end def paypal_checkout plan = Plan.find(params[:plan_id]) subscription = plan.subscriptions.build redirect_to subscription.paypal.checkout_url( return_url: new_subscription_url(:plan_id => plan.id), cancel_url: root_url ) end
models/subscription.rb
attr_accessor :stripe_card_token, :paypal_payment_token def save_with_payment if valid? if paypal_payment_token.present? save_with_paypal_payment else save_with_stripe_payment end end end def paypal PaypalPayment.new(self) end def save_with_paypal_payment response = paypal.make_recurring self.paypal_recurring_profile_token = response.profile_id save! end def save_with_stripe_payment customer = Stripe::Customer.create(description: email, plan: plan_id, card: stripe_card_token) self.stripe_customer_token = customer.id save! rescue Stripe::InvalidRequestError => e logger.error "Stripe error while creating customer: #{e.message}" errors.add :base, "There was a problem with your credit card." false end def payment_provided? stripe_card_token.present? || paypal_payment_token.present? end
models/paypal_payment.rb
class PaypalPayment def initialize(subscription) @subscription = subscription end def checkout_details process :checkout_details end def checkout_url(options) process(:checkout, options).checkout_url end def make_recurring process :request_payment process :create_recurring_profile, period: :monthly, frequency: 1, start_at: Time.zone.now end private def process(action, options = {}) options = options.reverse_merge( token: @subscription.paypal_payment_token, payer_id: @subscription.paypal_customer_token, description: @subscription.plan.name, amount: @subscription.plan.price, currency: "USD" ) response = PayPal::Recurring.new(options).send(action) raise response.errors.inspect if response.errors.present? response end end
subscriptions/new.html.erb
<%= f.hidden_field :plan_id %> <%= f.hidden_field :stripe_card_token %> <%= f.hidden_field :paypal_customer_token %> <%= f.hidden_field :paypal_payment_token %> <% unless @subscription.payment_provided? %> <div class="field"> <%= radio_button_tag :pay_with, :card, true %> <%= label_tag :pay_with_card do %> <%= image_tag "visa.png" %> <%= image_tag "mastercard.png" %> <%= image_tag "discover.png" %> <%= image_tag "american_express.png" %> <%= image_tag "jcb.png" %> <% end %> <%= radio_button_tag :pay_with, :paypal %> <%= label_tag :pay_with_paypal do %> <%= image_tag "paypal.png" %> <% end %> </div> <% end %> <div id="paypal_checkout" style="display:none"> <%= link_to image_tag("https://www.paypal.com/en_US/i/btn/btn_xpressCheckout.gif"), paypal_checkout_path(plan_id: @subscription.plan_id) %> </div> <div id="billing_fields"> <div class="field"> <%= f.label :email %> <%= f.text_field :email %> </div> <% if @subscription.payment_provided? %> Payment has been provided. Click "Subscribe" to complete the subscription. <% else %> <div class="field"> <%= label_tag :card_number, "Credit Card Number" %> <%= text_field_tag :card_number, nil, name: nil %> </div> <div class="field"> <%= label_tag :card_code, "Security Code on Card (CVV)" %> <%= text_field_tag :card_code, nil, name: nil %> </div> <div class="field"> <%= label_tag :card_month, "Card Expiration" %> <%= select_month nil, {add_month_numbers: true}, {name: nil, id: "card_month"} %> <%= select_year nil, {start_year: Date.today.year, end_year: Date.today.year+15}, {name: nil, id: "card_year"} %> </div> <% end %> <div id="stripe_error"> <noscript>JavaScript is not enabled and is required for this form. First enable it in your web browser settings.</noscript> </div> <div class="actions"> <%= f.submit "Subscribe" %> </div> </div>
app/assets/javascripts/subscriptions.js.coffee
# ... $('#pay_with_paypal').click -> $('#paypal_checkout').show() $('#billing_fields').hide() true $('#pay_with_card').click -> $('#paypal_checkout').hide() $('#billing_fields').show() true