cancel
Showing results for 
Search instead for 
Did you mean: 

Adding the shipping, billing address to a transaction using ActiveMerchant

Hello there,

 

Our project is built using ruby on rails 3, using the ActiveMerchant gem. We're using AIM for one-off credit card transactions, and CIM for stored card transactions.

 

Our application is processing payments just fine using both gateways. Our two flows are as follows:

1. AIM: Customer picks product, gets to purchase page, enters Credit card info, and clicks purchase. Transaction is created on the server side and is submitted to authorize.net. User is re-directed to success or error page depending on response from server.

 

2. CIM: Returning customer picks product, gets to purchase page. "User stored card" is checked. They click purchase. Transactions is created on the server side using create_profile_transaction and submitting customer_profile_id and customer_payment_profile_id to retrieve card information.

 

We're interested in saving the User name as shipping address for transactions on both gateways.

 

CIM: I have implemented create_customer_shipping_address, but this feels like overkill for our app. Is this the best way to submit shipping addresses with transactions on the CIM gateway? 

 

 I've combed the ActiveMerchant documentation, but cannot figure out how to submit a shipping-address "on-the-fly" when calling create_customer_profile_transaction. This Gist seems to hint that it is possible... Does anyone know how to do this?

 

Our code:

class StoredCardTransaction < GatewayTransaction
  delegate :customer_profile_id, :customer_payment_profile_id, :to => :user

  def gateway_purchase
    StoredCardPaymentGateway.purchase(self.amount, self.customer_profile_id, self.customer_payment_profile_id)
  end
end

 

 

AIM: Transaction is created and submitted using gateway.purchase(amount, credit_card, options). Currently, our options hash is  

{:order_id => generate_unique_order_id }

 

 

I've tried adding shipping address like so

{:order_id => generate_unique_order_id,
    :shipping_address => {:first_name => "Fred", :last_name => "Flintstone"}
}

 That didn't work.

 

Again, the activeMerchant documentation is severly lacking

The official Authorize.net documentation seems to indicate that a multitude of attributes, including shipping/billing addresses, can be included on purchase() and create_customer_profile_transaction() respectively. However both the SOAP and XML documentation also show six different 'amount' fields on the same post-submit with no indication of how the data is organized. I feel like I'm shooting in the dark right now.

 

Our code:

class CreditCardTransaction < GatewayTransaction
  extend ActiveSupport::Memoizable
  attr_accessor :credit_card, :store_card

  validates :amount, :presence => true, :numericality => {:greater_than => 0}
  validates :currency, :gateway_transaction_id, :gateway_response_message, :presence => true
  validates :last_digits, :presence => true

  def gateway_purchase(options={})
    raise ArgumentError, "credit card has not been set" unless self.credit_card.present?

    options[:order_id] ||= generate_unique_order_id
    self.last_digits = self.credit_card.number[-4..-1]
    result = PaymentGateway.purchase(self.amount, self.credit_card, options)
    return result unless result.success?

    self.user.store_credit_card!(self.credit_card) if self.store_card
    result
  end

  def generate_unique_order_id
    UUIDTools::UUID.timestamp_create.to_s
  end
end

 

I hope this explanation is sufficient. I can provide more information if requested.

 

Thanks for taking the time to take a look at this. I appreciate it.

 

-Philip Kobernik

2 REPLIES 2

Good news!

 

Some transactions showed up on our test gateway that indicate that I'm doing it correctly on the AIM gateway. The code below is storing the card as the billing name by default, and storing the shipping_address first/last name correctly:

 

PaymentGateway.purchase(
        13.37,
        credit_card,
        {:order_id => generate_unique_order_id,
          :shipping_address => {
            :first_name => "Shipping",
            :last_name => "PaymentGatewayIntegration"
          }
        }
      )

 So now I just need to figure out the best way to store shipping_address for the CIM gateway.

 

Hooray for small victories!

 

-Philip

More good news!

 

One of my 'stabbing in the dark' attempts worked, and my code is successfully saving billing and shipping names correctly on our transactions that use the CIM gateway.

 

Code is below.

 

On the user model:

 

def store_credit_card!(credit_card)
    transaction_info = {
      :billing_address => { :first_name => credit_card.first_name, :last_name => credit_card.last_name }
    }
    result = StoredCardPaymentGateway.store(credit_card, transaction_info)
    if result.success?
      self.customer_profile_id = result.customer_profile_id
      self.customer_payment_profile_id = result.customer_payment_profile_id
      self.last_four = credit_card.number[-4..-1]
      self.save!
      create_customer_shipping_address(result.customer_profile_id) unless self.customer_address_id
    else
      log_msg = "\n\n\n**##** Credit card storage failed **##** "
      logger.error log_msg
    end
    result
  end

  def create_customer_shipping_address(customer_profile_id)
    shipping_profile = {
      :customer_profile_id => customer_profile_id,
      :address => {
        :first_name => first_name,
        :last_name => last_name
      }
    }
    gateway = StoredCardPaymentGateway.gateway
    response = gateway.create_customer_shipping_address(shipping_profile)
    result = ShippingStorageResult.new(response)

    self.customer_address_id = result.customer_address_id if result.success?
    self.save
  end
end

class ShippingStorageResult
  attr_accessor :success, :customer_address_id, :error_code, :message

  def initialize(response)
    if response.success?
      self.success = true
      self.customer_address_id = response.params["customer_address_id"]
    else
      self.success = false
      self.error_code = response.params["messages"]["result_code"]
    end
    self.message = response.params["messages"]["message"]["text"]
  end

  def success?
    success
  end

 The above code makes an attempt to store the shipping address (just first/last name here) if the card-storage operation is successful.

 

If saving shipping address is successful, the gateway returns a customer_address_id that we store on the user.

 

Then... on the purchase method for CIM Gateway transaction...

 

create_customer_profile_transaction(:transaction => {
                                              :customer_profile_id => customer_profile_id,
                                              :customer_payment_profile_id => customer_payment_profile_id,
                                              :type => :auth_capture,
                                              :amount => amount(money),
                                              :customer_address_id => options[:customer_address_id] })

 

This is storing the shipping address correctly on the authorize.net CIM Gateway.

 

Near the top of the first code snippet, you can see how I'm storing the billing address in a hash called 'transaction_info' and submitting it as the 'options' parameter on StoredCardGateway.store(). If the parameters look strange to you, its beacuse we're using this module to extend ActiveMerchant's AuthorizeNetCimGateway

 

I hope this can help you out if you find yourself confounded by lacking documentation for ActiveMerchant && Authorize.net.

 

Cheers,

Philip