require 'webpay'
module Spree
# not precise notation, but required for Rails convention
class PaymentMethod::Webpay < PaymentMethod
preference :secret_key, :string
preference :publishable_key, :string
# Meta ================
def payment_source_class
CreditCard
end
def method_type
'webpay'
end
def payment_profiles_supported?
true
end
def source_required?
true
end
# Copied from spree-core gateway.rb
def reusable_sources(order)
if order.completed?
sources_by_order order
else
if order.user_id
self.credit_cards.where(user_id: order.user_id).with_payment_profile
else
[]
end
end
end
def supports?(source)
@account_info ||= client.account.retrieve
brand_name =
case source.brand
when 'visa' then 'Visa'
when 'master' then 'MasterCard'
when 'diners_club' then 'Diners Club'
when 'american_express' then 'American Express'
when 'discover' then 'Discover'
when 'jcb' then 'JCB'
end
@account_info.card_types_supported.include?(brand_name)
end
# Payment related methods ================
# Options are ignored unless they are mentioned in parameters list.
CVC_CODE_TRANSLATOR = {
'pass' => 'M',
'fail' => 'N',
'unchecked' => 'P'
}
# Performs an authorization, which reserves the funds on the customer's credit card, but does not
# charge the card.
#
# ==== Parameters
#
# * money -- The amount to be authorized as an Integer value in cents.
# * paysource -- The CreditCard or Check details for the transaction.
def authorize(money, paysource, options = {})
create_charge(money, paysource, false)
end
# Perform a purchase, which is essentially an authorization and capture in a single operation.
#
# ==== Parameters
#
# * money -- The amount to be purchased as an Integer value in cents.
# * paysource -- The CreditCard or Check details for the transaction.
def purchase(money, paysource, options = {})
create_charge(money, paysource, true)
end
# Captures the funds from an authorized transaction.
#
# ==== Parameters
#
# * money -- The amount to be captured as an Integer value in cents.
# * authorization -- The authorization returned from the previous authorize request.
def capture(money, authorization, options = {})
wrap_in_active_merchant_response { client.charge.capture(id: authorization, amount: money) }
end
# Void a previous transaction
#
# ==== Parameters
#
# * authorization - The authorization returned from the previous authorize request.
def void(authorization, _source, options = {})
wrap_in_active_merchant_response { client.charge.refund(authorization) }
end
# Refund a transaction.
#
# This transaction indicates to the gateway that
# money should flow from the merchant to the customer.
#
# ==== Parameters
#
# * money -- The amount to be credited to the customer as an Integer value.
# * identification -- The ID of the original transaction against which the refund is being issued.
def refund(money, _source, identification, options = {})
wrap_in_active_merchant_response do
charge = client.charge.retrieve(identification)
client.charge.refund(id: identification, amount: charge.amount.to_i - charge.amount_refunded.to_i - money)
end
end
def credit(money, source, identification, options = {})
refund(money, source, identification, options)
end
def create_profile(payment)
return if payment.source.gateway_customer_profile_id.present?
begin
customer = client.customer.create(
email: payment.order.email,
description: payment.order.name,
card: payment.source.gateway_payment_profile_id,
)
payment.source.update_attributes!({
gateway_customer_profile_id: customer.id,
gateway_payment_profile_id: nil
})
rescue WebPay::ApiError => e
payment.send(:gateway_error, e.respond_to?(:data) ? e.data.error.message : e.message)
end
end
private
def sources_by_order(order)
source_ids = order.payments.where(source_type: payment_source_class.to_s, payment_method_id: self.id).pluck(:source_id).uniq
payment_source_class.where(id: source_ids).with_payment_profile
end
# In this gateway, what we call 'secret_key' is the 'login'
def client
@client ||= WebPay.new(preferred_secret_key)
end
def create_charge(money, paysource, capture)
params = {
amount: money,
currency: 'jpy',
capture: capture,
}
if payment_id = paysource.gateway_payment_profile_id.presence
params[:card] = payment_id
else
params[:customer] = paysource.gateway_customer_profile_id.presence
end
wrap_in_active_merchant_response { client.charge.create(params) }
end
def wrap_in_active_merchant_response(&block)
begin
response = block.call
ActiveMerchant::Billing::Response.new(!response.failure_message,
response.failure_message || "Transaction approved",
response.to_h,
test: !response.livemode,
authorization: response.id,
avs_result: nil, # WebPay does not check avs
cvv_result: CVC_CODE_TRANSLATOR[response.card.cvc_check]
)
rescue WebPay::ApiError => e
ActiveMerchant::Billing::Response.new(false,
e.respond_to?(:data) ? e.data.error.message : e.message,
{},
test: false,
authorization: e.respond_to?(:data) ? e.data.error.charge : nil,
avs_result: nil,
cvv_result: nil
)
end
end
end
end