Paypal Recurring Gem - suspend payment

2020-07-19 04:05发布

问题:

I am looking to setup payment suspension for the paypal recurring gem (followed rails cast). I'm not sure if setting up IPN is required as there's no mention of it in the docs for the gem. The code I currently have takes no action.

I defined cancel recurring in the model, though I am not sure how to finish the code as it is hard for me to understand how this all works. This question has been asked by others but there are no answers to it.

If someone has the time to asset me that would be great!

The question is how to suspend/cancel the user recurring payment.

Paypal_payment.rb:

   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

    def suspend
        process :suspend, :profile_id => @subscription.paypal_recurring_profile_token
    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 controller:

  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 create
    @subscription = Subscription.new(params[:subscription])
    if @subscription.save_with_payment
      redirect_to @subscription, :notice => "Thank you for subscribing!"
    else
      render :new
    end
  end

  def show
    @subscription = Subscription.find(params[:id])
  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

    def updatesubscription
      @user = current_user
      @customer = Stripe::Customer.retrieve(@user.subscription.stripe_customer_token)
      @customer.update_subscription(:plan => "1", :prorate => true)
     current_user.save!
      flash.alert = 'Your subscription has been updated!'
      redirect_to root_url
     end

     def cancelsubscription
       @user = current_user
         @customer = Stripe::Customer.retrieve(@user.subscription.stripe_customer_token)
         @customer.cancel_subscription()
         current_user.save!
         flash.alert = 'Your subscription has been cancelled successfully!'
         redirect_to root_url
       end

       def showcard
         @user = current_user
         Stripe::Customer.retrieve(@user.subscription.stripe_customer_token).cards.all()
       end

       def changecard
           @user = current_user       
           @customer = Stripe::Customer.retrieve(@user.subscription.stripe_customer_token)

             card = @customer.cards.create({
               :card => @user.subscription.stripe_customer_token
             })

             @customer.default_card = card
             @customer.save
           end

           def suspend
             @user = current_user
             @user.subscription.suspend_paypal
           end


         def updatebilling
             @user = current_user
             customer = Stripe::Customer.retrieve(@user.subscription.stripe_customer_token)
             customer.cards.retrieve("#{@user.subscription.stripe_card_id}").delete()
             customer.cards.create({
                   card: {
                   number: params[:user][:scardnumber],
                   exp_month: params[:user][:sexp_month],
                   exp_year: params[:user][:sexp_year],
                   cvc: params[:user][:scvc],
                   name: params[:user][:sname],
                   address_line1: params[:user][:sbilling_address1],
                   address_line2: params[:user][:sbilling_address2],
                   address_city: params[:user][:saddress_city],
                   address_zip: params[:user][:saddress_zip],
                   address_state: params[:user][:saddress_state],
                   address_country: params[:user][:saddress_country]
                   }
                 })
                 if customer.save!
                   @user.stripe_card_id = customer.active_card.id
                   @user.save!
                   flash.alert = 'Billing information updated successfully!'
                   redirect_to root_url
                 else
                   flash.alert = 'Stripe error'
                   redirect_to root_url
                 end
               end
end

Subscription Model:

  belongs_to :plan
  belongs_to :subscription
  belongs_to :user

  validates_presence_of :plan_id
  validates_presence_of :email

  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

  def suspend_paypal
    paypal.suspend
    self.status = "canceled"
    save
  end
end

Routes:

  get "subscriptions/cancelsubscription"
  get "subscriptions/updatesubscription"
  get "subscriptions/changecard"
  get "subscriptions/suspend"
  get "subscriptions/updatebilling"

  resources :charges
  resources :subscriptions
  resources :plans
  get 'paypal/checkout', to: 'subscriptions#paypal_checkout'

View:

<%= link_to "Suspend paypal", subscriptions_suspend_path, :data => { :confirm => "Are you sure?" } %>

回答1:

This PaypalPayment is a kind of wrapper for the paypal-recurring gem. So all of the methods in this class just prepare and delegate to PayPal::Recurring that's why all of the methods just call the 'process' method which instantiate and pass the action.

So for suspending/cancelling you just need to add a method for each of this actions. As the document states you need to do this for cancel

ppr = PayPal::Recurring.new(:profile_id => "I-VCEL6TRG35CU")
ppr.suspend

So for your PaypalPayment class it would look like this:

def suspend
    process :suspend, :profile_id => @subscription.paypal_recurring_profile_token
end

So your model subscription.rb

def suspend_paypal
  paypal.suspend
  self.status = "canceled"
  save
end

And the controller susbcription_controller.rb

def suspend
  current_user.suspend_paypal
end

About IPN, I don't think its necessary if the user suspend through your site, but as the user might cancel it directly through paypal you have to handle this case so the User don't stop paying but keep with an active subscription.