lib/active_merchant/billing/gateways/plugnpay.rb
module ActiveMerchant
module Billing
class PlugnpayGateway < Gateway
class PlugnpayPostData < PostData
# Fields that will be sent even if they are blank
self.required_fields = %i[publisher_name publisher_password
card_amount card_name card_number card_exp orderID]
end
self.live_url = self.test_url = 'https://pay1.plugnpay.com/payment/pnpremote.cgi'
CARD_CODE_MESSAGES = {
'M' => 'Card verification number matched',
'N' => "Card verification number didn't match",
'P' => 'Card verification number was not processed',
'S' => 'Card verification number should be on card but was not indicated',
'U' => 'Issuer was not certified for card verification'
}
CARD_CODE_ERRORS = %w(N S)
AVS_MESSAGES = {
'A' => 'Street address matches billing information, zip/postal code does not',
'B' => 'Address information not provided for address verification check',
'E' => 'Address verification service error',
'G' => 'Non-U.S. card-issuing bank',
'N' => 'Neither street address nor zip/postal match billing information',
'P' => 'Address verification not applicable for this transaction',
'R' => 'Payment gateway was unavailable or timed out',
'S' => 'Address verification service not supported by issuer',
'U' => 'Address information is unavailable',
'W' => '9-digit zip/postal code matches billing information, street address does not',
'X' => 'Street address and 9-digit zip/postal code matches billing information',
'Y' => 'Street address and 5-digit zip/postal code matches billing information',
'Z' => '5-digit zip/postal code matches billing information, street address does not'
}
AVS_ERRORS = %w(A E N R W Z)
PAYMENT_GATEWAY_RESPONSES = {
'P01' => 'AVS Mismatch Failure',
'P02' => 'CVV2 Mismatch Failure',
'P21' => 'Transaction may not be marked',
'P30' => 'Test Tran. Bad Card',
'P35' => 'Test Tran. Problem',
'P40' => 'Username already exists',
'P41' => 'Username is blank',
'P50' => 'Fraud Screen Failure',
'P51' => 'Missing PIN Code',
'P52' => 'Invalid Bank Acct. No.',
'P53' => 'Invalid Bank Routing No.',
'P54' => 'Invalid/Missing Check No.',
'P55' => 'Invalid Credit Card No.',
'P56' => 'Invalid CVV2/CVC2 No.',
'P57' => 'Expired. CC Exp. Date',
'P58' => 'Missing Data',
'P59' => 'Missing Email Address',
'P60' => 'Zip Code does not match Billing State.',
'P61' => 'Invalid Billing Zip Code',
'P62' => 'Zip Code does not match Shipping State.',
'P63' => 'Invalid Shipping Zip Code',
'P64' => 'Invalid Credit Card CVV2/CVC2 Format.',
'P65' => 'Maximum number of attempts has been exceeded.',
'P66' => 'Credit Card number has been flagged and can not be used to access this service.',
'P67' => 'IP Address is on Blocked List.',
'P68' => 'Billing country does not match ipaddress country.',
'P69' => 'US based ipaddresses are currently blocked.',
'P70' => 'Credit Cards issued from this bank are currently not being accepted.',
'P71' => 'Credit Cards issued from this bank are currently not being accepted.',
'P72' => 'Daily volume exceeded.',
'P73' => 'Too many transactions within allotted time.',
'P91' => 'Missing/incorrect password',
'P92' => 'Account not configured for mobil administration',
'P93' => 'IP Not registered to username.',
'P94' => 'Mode not permitted for this account.',
'P95' => 'Currently Blank',
'P96' => 'Currently Blank',
'P97' => 'Processor not responding',
'P98' => 'Missing merchant/publisher name',
'P99' => 'Currently Blank'
}
TRANSACTIONS = {
authorization: 'auth',
purchase: 'auth',
capture: 'mark',
void: 'void',
refund: 'return',
credit: 'newreturn'
}
SUCCESS_CODES = %w[pending success]
FAILURE_CODES = %w[badcard fraud]
self.default_currency = 'USD'
self.supported_countries = ['US']
self.supported_cardtypes = %i[visa master american_express discover]
self.homepage_url = 'http://www.plugnpay.com/'
self.display_name = "Plug'n Pay"
def initialize(options = {})
requires!(options, :login, :password)
super
end
def purchase(money, creditcard, options = {})
post = PlugnpayPostData.new
add_amount(post, money, options)
add_creditcard(post, creditcard)
add_addresses(post, options)
add_invoice_data(post, options)
add_customer_data(post, options)
post[:authtype] = 'authpostauth'
commit(:authorization, post)
end
def authorize(money, creditcard, options = {})
post = PlugnpayPostData.new
add_amount(post, money, options)
add_creditcard(post, creditcard)
add_addresses(post, options)
add_invoice_data(post, options)
add_customer_data(post, options)
post[:authtype] = 'authonly'
commit(:authorization, post)
end
def capture(money, authorization, options = {})
post = PlugnpayPostData.new
post[:orderID] = authorization
add_amount(post, money, options)
add_customer_data(post, options)
commit(:capture, post)
end
def void(authorization, options = {})
post = PlugnpayPostData.new
post[:orderID] = authorization
post[:txn_type] = 'auth'
commit(:void, post)
end
def credit(money, identification_or_creditcard, options = {})
post = PlugnpayPostData.new
add_amount(post, money, options)
if identification_or_creditcard.is_a?(String)
ActiveMerchant.deprecated CREDIT_DEPRECATION_MESSAGE
refund(money, identification_or_creditcard, options)
else
add_creditcard(post, identification_or_creditcard)
add_addresses(post, options)
add_customer_data(post, options)
commit(:credit, post)
end
end
def refund(money, reference, options = {})
post = PlugnpayPostData.new
add_amount(post, money, options)
post[:orderID] = reference
commit(:refund, post)
end
private
def commit(action, post)
response = parse(ssl_post(self.live_url, post_data(action, post)))
success = SUCCESS_CODES.include?(response[:finalstatus])
message = success ? 'Success' : message_from(response)
Response.new(
success,
message,
response,
test: test?,
authorization: response[:orderid],
avs_result: { code: response[:avs_code] },
cvv_result: response[:cvvresp]
)
end
def parse(body)
body = CGI.unescape(body)
results = {}
body.split('&').collect { |e| e.split('=') }.each do |key, value|
results[key.downcase.to_sym] = normalize(value.to_s.strip)
end
results.delete(:publisher_password)
results[:avs_message] = AVS_MESSAGES[results[:avs_code]] if results[:avs_code]
results[:card_code_message] = CARD_CODE_MESSAGES[results[:cvvresp]] if results[:cvvresp]
results
end
def post_data(action, post)
post[:mode] = TRANSACTIONS[action]
post[:convert] = 'underscores'
post[:app_level] = 0
post[:publisher_name] = @options[:login]
post[:publisher_password] = @options[:password]
post.to_s
end
def add_creditcard(post, creditcard)
post[:card_number] = creditcard.number
post[:card_cvv] = creditcard.verification_value
post[:card_exp] = expdate(creditcard)
post[:card_name] = creditcard.name.slice(0..38)
end
def add_customer_data(post, options)
post[:email] = options[:email]
post[:dontsndmail] = 'yes' unless options[:send_email_confirmation]
post[:ipaddress] = options[:ip]
end
def add_invoice_data(post, options)
post[:shipping] = amount(options[:shipping]) unless options[:shipping].blank?
post[:tax] = amount(options[:tax]) unless options[:tax].blank?
end
def add_addresses(post, options)
if address = options[:billing_address] || options[:address]
post[:card_address1] = address[:address1]
post[:card_zip] = address[:zip]
post[:card_city] = address[:city]
post[:card_country] = address[:country]
post[:phone] = address[:phone]
case address[:country]
when 'US', 'CA'
post[:card_state] = address[:state]
else
post[:card_state] = 'ZZ'
post[:card_prov] = address[:state]
end
end
if shipping_address = options[:shipping_address] || address
post[:shipname] = shipping_address[:name]
post[:address1] = shipping_address[:address1]
post[:address2] = shipping_address[:address2]
post[:city] = shipping_address[:city]
case shipping_address[:country]
when 'US', 'CA'
post[:state] = shipping_address[:state]
else
post[:state] = 'ZZ'
post[:province] = shipping_address[:state]
end
post[:country] = shipping_address[:country]
post[:zip] = shipping_address[:zip]
end
end
def add_amount(post, money, options)
post[:card_amount] = amount(money)
post[:currency] = options[:currency] || currency(money)
end
def message_from(results)
PAYMENT_GATEWAY_RESPONSES[results[:resp_code]]
end
def expdate(creditcard)
year = sprintf('%.4i', creditcard.year)
month = sprintf('%.2i', creditcard.month)
"#{month}/#{year[-2..-1]}"
end
end
end
end