Bradley Priest

Pxpay - Payment Express for Rails 15 May 2011

Building websites in NZ invariably leads to integrating our most popular Payment Solution, Payment Express (DPS) over and over again.

Payment Express has two major options when it comes to online payments, the self-hosted version PxPost and the DPS-hosted version PxPay.

If you want to deal with credit card payments yourself using PxPost, the brilliant ActiveMerchant has the PxPost gateway built into it.

However, for DPS-hosted payments, I couldn’t find any Ruby implementation, so I put one together myself.

I’ve put together the Pxpay gem.

Obviously, dealing with payment data is something that has serious repercussions if something goes wrong, so be careful.

With that out of the way let’s take a look at how it works.

First things first, make sure you set up a development account with Payment Express https://www.paymentexpress.com/pxmi/apply

With that out of the way install the gem and generate the config

This installs the gem and a pxpay.rb initializer to your config folder.

$ gem install pxpay
$ rails generate pxpay:install

Firstly, make sure you update the initializer file with your credentials and optionally add the success and failure URLs for your app.

PxPay currently requires the nokogiri and rest-client gems.

>>require 'nokogiri'
>>require 'pxpay'
>>request = Pxpay::Request.new( 1, 12.00 )
  =>#<Pxpay::Request:0x00000101c9a840>
>>request.url
  => "https://sec2.paymentexpress.com/pxpay/pxpay.aspx?userid=Fake_Dev&request=xxxxxxxxxx"

The Pxpay::Request object takes a unique ID, a price and an optional hash of arguments.

The important options are :url_success and :url_failure.

Other options include :merchant_reference, :currency and :email address.

Check the documentation for all the options.

In a rails app pass the arguments into PxPay::Request and redirect the customer to the returned URL.

order.rb

def url
  @url = Pxpay::Request.new( id , price, {
    :email => user.email,
    :url_success => "http://example.com/orders/#{id}/success",
    :url_failure => "http://example.com/orders/#{id}/failure"
  })
end

Payment Express will process the payments then redirect the customer back to either the success or failure URL.

orders_controller.rb

def success
  response = Pxpay::Response.new(params).response
  hash = response.to_hash
  ## Do something with the results hash
end

N.B. There is a minor caveat here: Payment Express includes a system called fail-proof result notification where as soon as the customer has finished the transaction they will send a background request.

This means your success/failure URL will be hit at least twice for each transaction, so you must allow for this in your code. See here for details.

That’s pretty much it. Any questions/problems hit me up on Github

Simple Rails Snippets 13 Jan 2011

There has been a bit of development around content micro-management in the Rails ecosystem lately.

Thoughtbot just released their hosted service Copycopter. Quickleft brought out the Regulate gem.

It’s really good to see some simple solutions to what should really be a simple problem.

For me, even these seem a bit much, if you just want to add really basic snippet management it really isn’t that hard to from scratch. I can’t promise you this is the best way to do it, but it works well for me, comments are greatly appreciated.

Start by creating a new Snippet model. I’m just going to use Ryan Bates’ amazing nifty-generators. All you really need is a name and content. I’m throwing in a status for drafts as well.

$ rails generate nifty:scaffold Snippet title:string snippet:text status:string

Now let’s set up the basics: A simple helper

snippet_helper.rb

def snippet_for(name, default = nil)
  Rails.cache.fetch("snippet::"+name.to_s)) do
    Snippet.published.find_by_title(name.to_s) || default || "Snippet for #{name.to_s} missing"
  end
end

alias_method :s, :snippet_for

In the snippet helper I’m defining a snippet_for method:

This takes the name of the snippet, and an optional default value. Make sure you use descriptive names here, you’ll thank yourself later.

The helper will try to find a value first by searching the rails cache, followed by the snippet database, followed by the optional default and lastly will show a snippet not found message.

In the last line I’m aliasing snippet_for(…) to s(..) just for brevity

models/snippet.rb

class Snippet < ActiveModel
  after_save :clear_cache
  scope :published where(:status => 'published')

  def to_s
    snippet
  end

  def clear_cache
    Rails.cache.delete("snippet::"+title)
  end
end

The snippet model contains a simple published scope. And an after_save callback to allow for snippet updating. If you starting enhancing your snippet model you’ll probably want to implement a Rails sweeper.

Now in your code when you need a snippet

= snippet_for(:call_to_action, 'Click Me')
= s(:call_to_action, 'Click Me')