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
09-21-2011 04:05 PM - edited 09-21-2011 04:06 PM
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
09-21-2011 06:09 PM
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
endThe 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
09-23-2011 10:40 AM - edited 09-23-2011 10:42 AM