为PayPal创建IPN URL

6

我使用paypal recurring gem设置了PayPal,现在需要帮助理解如何实现PayPal IPN。

如果您能提供任何帮助,我将不胜感激,因为这是我项目的最后一步。我需要设置IPN,以便当用户从其PayPal账户取消/暂停计费时,它将在我的数据库中显示为已取消。

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

    def reactivate
        process :reactivate, :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,
        ipn_url: "http://mydomain.com/paypal/ipn",
        currency: "USD"
      )
      response = PayPal::Recurring.new(options).send(action)
      raise response.errors.inspect if response.errors.present?
      response
    end
  end

Subscription.rb:

  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 expires_at
    self.updated_at + plan.duration.days
  end

  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
    save
  end


  def reactivate_paypal
    paypal.reactivate
    save
  end

  def update_card(subscriber, card_info)
  token = Stripe::Token.create(
    card: {
      number: card_info[:number],
      exp_month: card_info[:exp_month],
      exp_year: card_info[:exp_year],
      cvc: card_info[:cvc]
    }
  )
  customer = Stripe::Customer.retrieve(user.subscription.stripe_customer_token)
  card = customer.cards.create(card: token.id)
  card.save
  customer.default_card = card.id
  customer.save
  rescue Stripe::InvalidRequestError => e
    logger.error "Stripe error while updating card info: #{e.message}"
    errors.add :base, "#{e.message}"
    false
  end
end

订阅控制器:

  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)
      if @user.subscription.plan_id == 12
      @customer.update_subscription(:plan => "1", :prorate => true)
      current_user.subscription.update_attributes(:plan_id => 1)
      flash.alert = 'Your subscription has been changed to monthly!'
      redirect_to root_url
    elsif @user.subscription.plan_id == 1
      @customer.update_subscription(:plan => "12", :prorate => true)
      current_user.subscription.update_attributes(:plan_id => 12)
     current_user.save!
      flash.alert = 'Your subscription has been changed to annually!'
      redirect_to root_url
    end
     end

     def cancelsubscription
       @user = current_user
         @customer = Stripe::Customer.retrieve(@user.subscription.stripe_customer_token)
         @customer.cancel_subscription()
         current_user.subscription.update_attributes(:cancelled => 1)
         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 suspend
             @user = current_user
             @user.subscription.suspend_paypal
             current_user.subscription.update_attributes(:cancelled => 1)
               flash.alert = 'Billing has been suspended!'
                redirect_to root_url
           end

           def reactivate
             @user = current_user
             @user.subscription.reactivate_paypal
             current_user.subscription.update_attributes(:cancelled => nil)
               flash.alert = 'Billing has been activated!'
                redirect_to root_url
           end


               def edit_card
                 @user = current_user
               end

               def update_card
                 @user = current_user
                 card_info = {
                   name:    params[:name],
                   number:    params[:number],
                   exp_month: params[:date][:month],
                   exp_year:  params[:date][:year],
                   cvc:       params[:cvc]
                 }
                 if @user.subscription.update_card(@subscriber, card_info)
                   flash.alert = 'Saved. Your card information has been updated.'
                   redirect_to root_url
                 else
                   flash.alert = 'Stripe reported an error while updating your card. Please try again.'
                   redirect_to root_url
                 end
               end
end

路由:

  post "paypal/ipn" => "notifications#create"

付款通知控制器:

def index

  redirect_to root_url
end

   def create
     PaymentNotification.create!(:params => params, :status => params[:payment_status], :transaction_id => params[:txn_id])
     render :nothing => true
   end
 end

通知控制器:

  def create
            query = params
            query[:cmd] = "_notify-validate"
            if(response.body == "VERIFIED")
                Rails.logger.debug params.inspect "Notification is valid"
            end
           end
    end

注意PayPal“退款”黑客攻击(https://www.etsy.com/teams/7718/questions/discuss/12758905/)。 - GuyT
3个回答

0
你需要修复你的路由。
应该是:
  match '/paypal/ipn' => 'notifications#create', :via => [:get, :post], :as => 'notifications_create'

0
PayPal开发者网站上有一个IPN测试工具。您可以提供回调URL并发送样本调用。您需要先注册才能使用它。

0

所以你唯一的问题就是在测试账户中触发IPN吗??当你使用模拟器时,一切都按预期工作吗?

要在沙盒中使IPN工作,您需要做的就是确保在沙盒卖家帐户配置文件中启用了它。您可以像登录实际账户一样登录到http://sandbox.paypal.com沙盒账户。一旦进入账户,进入配置文件,然后进入IPN设置,以便您可以启用它并相应地设置URL。


问题是我应该如何定义我的IPN网址,以便我可以向PayPal沙盒提供IPN。 - Cornelius Wilson
这就是我刚刚解释的。 - Drew Angell
我知道如何启用/禁用IPN。我想知道是否需要在控制器中创建一个方法来创建IPN URL,以便我可以进行测试?我不确定如何生成IPN URL以便我可以与Paypal一起使用。 - Cornelius Wilson
你可以按照自己的意愿构建它。它只是某种脚本,可以通过公共URL(或至少向PayPal的IP地址开放)访问。IPN本质上只是一个POST,因此您只需要设置一个侦听器来相应地处理此POST数据即可。 - Drew Angell
1
嗨, Cornelius,我想知道你是否需要一条隧道。如果是这样,请尝试使用 https://ngrok.com/。如果不起作用,请告诉我。 - datnt
问题出在设置路由上。我设置错误了,但是现在隧道已经完美地工作了,谢谢。 - Cornelius Wilson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接