Skip to content

Instantly share code, notes, and snippets.

@itskingori
Last active October 25, 2016 17:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save itskingori/51be3c543dcba43a3a77 to your computer and use it in GitHub Desktop.
Save itskingori/51be3c543dcba43a3a77 to your computer and use it in GitHub Desktop.
Controller example on how to use Pesapal gem ...
module Account
module Payments
class PesapalController < ApplicationController
# GET /account/payments/pesapal/new
def new
# Configure transaction details
pesapal = Pesapal::Merchant.new
pesapal.order_details = {
amount: '<AMOUNT>',
description: '<DESCRIPTION>',
type: 'MERCHANT',
reference: '<REFERENCE>',
first_name:'<FIRST_NAME>',
last_name: '<LAST_NAME>',
email: '<EMAIL>',
phonenumber: '<PHONE_NUMBER>',
currency: 'KES_or_USD'
}
# Configure API settings while passing on the data that we need
pesapal.config = {
callback_url: pesapal_callback_account_payments_url,
consumer_key: Rails.application.secrets.pesapal_consumer_key,
consumer_secret: Rails.application.secrets.pesapal_consumer_secret
}
# Generate iframe
@order_url = pesapal.generate_order_url
end
# GET /account/payments/pesapal/callback
def callback
payment = Payment.new(
user: current_user,
reference_no: params.require(:pesapal_merchant_reference),
tracking_no: params.require(:pesapal_transaction_tracking_id),
amount: "amount",
currency_iso_code: "currency.iso_code",
status: 'pending'
)
if payment.save
redirect_to account_payments_url, notice: "Your payment of XXX has been completed but is still pending confirmation."
return
end
redirect_to new_account_payment_url, alert: "Your payment of XXX was not completed successfully."
end
# GET /account/payments/pesapal/ipn
def ipn
# Initialize Pesapal
pesapal = Pesapal::Merchant.new
pesapal.config = {
consumer_key: Rails.application.secrets.pesapal_consumer_key,
consumer_secret: Rails.application.secrets.pesapal_consumer_secret
}
# Get the pesapal transaction identifiers
reference_no = params.require(:pesapal_merchant_reference)
tracking_no = params.require(:pesapal_transaction_tracking_id)
# Make sure we respond to Pesapal and also, depending on the data from
# Pesapal ... apply our business logic appropriately.
respond_to do |format|
# Do nothing if we don't have what we need
if reference_no.blank? || tracking_no.blank?
format.html { render nothing: true }
# ... else apply business login ...
else
# Get hash with response from the `ipn_listener` helper
# method:
#
# {
# status: "<PAYMENT_STATUS>",
# response: "<IPN_RESPONSE>"
# }
ipn_response = pesapal.ipn_listener 'CHANGE', reference_no, tracking_no
# If payment exists in the DB so we proceed with updating it's
# status. INVALID indicates that the transaction with the reference
# you provided could not be found.
payment = ::Payment.find_by reference_no: reference_no, tracking_no: tracking_no
apply_ipn_update payment, ipn_response[:status] if payment
format.html { render text: ipn_response[:response] }
end
end
end
private
# Apply the update to the payment depending on the response
def apply_ipn_update(payment, ipn_status)
case ipn_status
when 'COMPLETED'
# do something
when 'FAILED'
# do something
when 'PENDING'
# do something
when 'INVALID'
# do somethimg
end
end
# Never trust parameters from the scary internet, only allow the white list through.
def payment_params
if params.key? :payment
params[:payment][:reference_no] = params[:pesapal_merchant_reference] unless params[:pesapal_merchant_reference].blank?
params[:payment][:tracking_no] = params[:pesapal_transaction_tracking_id] unless params[:pesapal_transaction_tracking_id].blank?
end
params.require(:payment).permit(:amount, :currency_iso_code, :payable_type, :reference_no, :tracking_no)
end
end
end
end
@Anyoks
Copy link

Anyoks commented Nov 13, 2015

Thanks! Pretty awesome.

But I'm getting this error when runnig pesapal = Pesapal::Merchant.new :

NoMethodError: undefined method pesapal_credentials for #<Rails::Application::Configuration:0x0000000347a3f8>

I have my pesapal.yml configured with the consumer_secret and consumer_key as well as restarted my server... any idea how to solve this?

@itskingori
Copy link
Author

@Anyoks I think you might have copied exactly how I set pesapal.config.

What version of Rails are you using? Rails.application.secrets.pesapal_consumer_key and Rails.application.secrets.pesapal_consumer_secret are created from ... config/secrets.yml ... which looks like this ...

# Be sure to restart your server when you modify this file. Your secret key is
# used for verifying the integrity of signed cookies. If you change this key,
# all old signed cookies will become invalid! Make sure the secret is at least
#30 characters and all random, no regular words or you'll be exposed to
# dictionary attacks. You can use `rake secret` to generate a secure secret key.
# Make sure the secrets in this file are kept private if you're sharing your
# code publicly. The secrets added to this file are accessible via
# Rails.application.secrets. For example:
#
# development:
#   secret_key_base: 3b7cd727ee24e8444053437c36cc66c3
#   some_api_key: SOMEKEY
#
# Rails.application.secrets.some_api_key returns SOMEKEY in the development
# environment.

default: &default
  pesapal_consumer_key: "<%= ENV['PESAPAL_CONSUMER_KEY'] %>"
  pesapal_consumer_secret:  "<%= ENV['PESAPAL_CONSUMER_SECRET'] %>"

development:
  <<: *default

test:
  <<: *default

# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
  <<: *default

But I think this is new. If using something else you might as well do something like this ...

# Configure API settings while passing on the data that we need
pesapal.config = {
  callback_url: pesapal_callback_account_payments_url,
  consumer_key: ENV['PESAPAL_CONSUMER_KEY'],
  consumer_secret: ENV['PESAPAL_CONSUMER_SECRET']
}

@itskingori
Copy link
Author

@Anyoks ... if you set pesapal.config you don't need to have pesapal.yml. I probably need to improve this aspect of the gem (or rather make it clearer).

@Anyoks
Copy link

Anyoks commented Nov 13, 2015

I'm using rails Rails 4.2.3
I have deleted the .yml file, plus I hadn't set the pesapal.config where should I set this? maybe I should try that. Even without the pesapal.yml I still get the error.

@Anyoks
Copy link

Anyoks commented Nov 13, 2015

I also found out that I need to add require pesapal in my controller for it recognise pesapal..

@Anyoks
Copy link

Anyoks commented Nov 13, 2015

Been looking through merchant.rb for where the error could be, all I have come up with is, the method pesapal_credentials is not defined. Here is the first occurrence (here is where I think the error emanates):
configure Rails.application.config.pesapal_credentials line 151

Other occurrences are in ``railtie.rblines10` and `19`
Could that be the issue?

@itskingori
Copy link
Author

@Anyoks, the gist up there shows you where and how to set pesapal.config, see line 21. :-)

You don't need to require 'pesapal' anywhere. If the gem is added in your Gemfile Rails will load it automatically. Just like other gems. Simple line like ...

# Make authenticated Pesapal API calls without the fuss!
gem 'pesapal'

@ikiugu
Copy link

ikiugu commented Oct 19, 2016

Thanks for this!!
I was lost for a moment after downloading the gem.
I have an issue though. Posting the transaction to pesapal works well but querying the status keeps returning nil.
@payment_status = pesapal.query_payment_status(@reference)
The variable @payment_status returns nil

@ikiugu
Copy link

ikiugu commented Oct 24, 2016

So I finally managed to get my development whole environment working. The issue was this http://developer.pesapal.com/forum/6-announcements/1788-https-protocol-enabled-on-our-sandbox-application. Since the api's now should be handled via HTTPS, the current gem could not work as it is. What made it work was two things (1) changing the url endpoint for development to https and (2) making sure in the query functions to always check ssl.
i.e

http = Net::HTTP.new(uri.host, uri.port)
#if @env == 'production'
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
#end
response = http.request(Net::HTTP::Get.new(uri.request_uri))

@itskingori
Copy link
Author

@Alfred-W I see! I used the sandbox a long time ago so wouldn't have caught that change. All my stuff is in production. Do you mind filing this as an issue? I can update the gem when I get time.

I also think that setting http.verify_mode to OpenSSL::SSL::VERIFY_NONE is a bad idea because we pretty much disregard the SSL certificate! 🙈 ... and I don't quite remember why I did this.

@itskingori
Copy link
Author

@Alfred-W Turns out the issue was already raised and someone had filed a PR! 🙈

Pole sana.

@itskingori
Copy link
Author

@Alfred-W Your issue has been fixed in v1.7.0. There's also v1.8.0 which addresses an important security concern.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment