Paypal express checkout using ActiveMerchant and Instant Payment Notification (IPN)

Instant Payment Notification (IPN) is a message service that notifies you of events related to PayPal transactions.

You can use IPN messages to automate back-office and administrative functions, such as fulfilling orders, tracking customers, and providing status and other transaction-related information or other kind of things, like emailing your customers upon purchase and offers.

Remember that if you are using Express Checkout or Direct Payment, the PayPal API notifies you of the status and details of the transaction immediately and automatically. This means that you need not use IPN for knowing the transaction status, because you will know the transaction status immediately.

This is the IPN flow:

image

1 A user clicks a PayPal button to kick off a checkout flow; your web application makes an API call; your back-office system makes an API call; or PayPal observes an event.

2 PayPal HTTP POSTs your listener a message that notifies you of this event.

3 Your listener returns an empty HTTP 200 response.

4 Your listener HTTP POSTs the complete, unaltered notification back to the PayPal.

Note: This message must contain the same fields, in the same order, as the original notification, all preceded by cmd=_notify-validate. Further, this message must use the same encoding as the original.

5 PayPal sends a single word back – either VERIFIED (if the message matches the original) or INVALID (if the message does not match the original).

IPN sends post data to the IPN listener so… have a route defined for this.

This is a basic payment method:


def payment
  item = Item.find(params[:id])
  response = EXPRESS_GATEWAY.setup_purchase(item.price_in_cents,{
    :ip                => request.remote_ip,
    :currency_code     => '...',
    :return_url        => success_url(item)
    :notify_url        => notify_url(item),
    :cancel_return_url => cancel_url(item)
    [...]
    })
  item.update_attribute :express_token, response.token
  redirect_to EXPRESS_GATEWAY.redirect_url_for(response.token)
end

This is the success url:


def success
  details = EXPRESS_GATEWAY.details_for(express_token)
  response = EXPRESS_GATEWAY.purchase(item.price_in_cents,{
    :ip               => request.remote_ip,
    :currency_code    => '...',
    :token            => express_token,
    :payer_id         => details.payer_id
    […]
  })

  if response.success?
    [...]
    flash[:success] = "Your transaction was successfully completed"
    [...]
  else
    flash[:error] = "Your transaction could not be compelted"
    [...]
  end

end

…and here is a typical handler for IPN with ActiveMerchant:


class BackendController < ApplicationController
  include ActiveMerchant::Billing::Integrations

  def paypal_ipn
    notify = Paypal::Notification.new(request.raw_post)

    order = Order.find(notify.item_id)

    if notify.acknowledge
      begin

        if notify.complete? and order.total == notify.amount
          order.status = 'success'

          shop.ship(order)
        else
          logger.error("Failed to verify Paypal's notification, please investigate")
        end

      rescue => e
        order.status        = 'failed'
        raise
      ensure
        order.save
      end
    end

    render :nothing
  end
end

One more thing: aknowledge method has to be called after a new ipn arrives. Paypal will verify that all the information we received are correct and will return a ok or a fail.

This is the acknowledge method on ActiveMerchant module:


def acknowledge(authcode = nil)
  payload =  raw

  response = ssl_post(Paypal.service_url + '?cmd=_notify-validate', payload,
    'Content-Length' => "#{payload.size}",
    'User-Agent'     => "Active Merchant -- http://activemerchant.org"
  )

  raise StandardError.new("Faulty paypal result: #{response}") unless ["VERIFIED", "INVALID"].include?(response)

  response == "VERIFIED"
end

Here are some references

ActiveMerchant Notification module: https://github.com/Shopify/active_merchant/blob/master/lib/active_merchant/billing/integrations/paypal/notification.rb

ActiveMerchant IPN Documentation: http://activemerchant.rubyforge.org/classes/ActiveMerchant/Billing/Integrations/Paypal/Notification.html

Paypal IPN Documentation: https://developer.paypal.com/docs/classic/ipn/gs_IPN/

Leave a Reply

Please Login to comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.