module ActiveMerchant #:nodoc: module Billing #:nodoc: class WepayGateway < Gateway self.test_url = 'https://stage.wepayapi.com/v2' self.live_url = 'https://wepayapi.com/v2' self.supported_countries = %w[US CA] self.supported_cardtypes = %i[visa master american_express discover] self.homepage_url = 'https://www.wepay.com/' self.default_currency = 'USD' self.display_name = 'WePay' def initialize(options = {}) requires!(options, :client_id, :account_id, :access_token) super(options) end def purchase(money, payment_method, options = {}) post = {} if payment_method.is_a?(String) MultiResponse.run do |r| r.process { authorize_with_token(post, money, payment_method, options) } r.process { capture(money, r.authorization, options) } end else MultiResponse.run do |r| r.process { store(payment_method, options) } r.process { authorize_with_token(post, money, r.authorization, options) } r.process { capture(money, r.authorization, options) } end end end def authorize(money, payment_method, options = {}) post = {} if payment_method.is_a?(String) authorize_with_token(post, money, payment_method, options) else MultiResponse.run do |r| r.process { store(payment_method, options) } r.process { authorize_with_token(post, money, r.authorization, options) } end end end def capture(money, identifier, options = {}) checkout_id, original_amount = split_authorization(identifier) post = {} post[:checkout_id] = checkout_id post[:amount] = amount(money) if money && (original_amount != amount(money)) commit('/checkout/capture', post, options) end def void(identifier, options = {}) post = {} post[:checkout_id] = split_authorization(identifier).first post[:cancel_reason] = (options[:description] || 'Void') commit('/checkout/cancel', post, options) end def refund(money, identifier, options = {}) checkout_id, original_amount = split_authorization(identifier) post = {} post[:checkout_id] = checkout_id post[:amount] = amount(money) if money && (original_amount != amount(money)) post[:refund_reason] = (options[:description] || 'Refund') post[:payer_email_message] = options[:payer_email_message] if options[:payer_email_message] post[:payee_email_message] = options[:payee_email_message] if options[:payee_email_message] commit('/checkout/refund', post, options) end def store(creditcard, options = {}) post = {} post[:client_id] = @options[:client_id] post[:user_name] = "#{creditcard.first_name} #{creditcard.last_name}" post[:email] = options[:email] || 'unspecified@example.com' post[:cc_number] = creditcard.number post[:cvv] = creditcard.verification_value unless options[:recurring] post[:expiration_month] = creditcard.month post[:expiration_year] = creditcard.year if (billing_address = (options[:billing_address] || options[:address])) post[:address] = {} post[:address]['address1'] = billing_address[:address1] if billing_address[:address1] post[:address]['city'] = billing_address[:city] if billing_address[:city] post[:address]['country'] = billing_address[:country] if billing_address[:country] post[:address]['region'] = billing_address[:state] if billing_address[:state] post[:address]['postal_code'] = billing_address[:zip] end if options[:recurring] == true post[:client_secret] = @options[:client_secret] commit('/credit_card/transfer', post, options) else post[:original_device] = options[:device_fingerprint] if options[:device_fingerprint] post[:original_ip] = options[:ip] if options[:ip] commit('/credit_card/create', post, options) end end def supports_scrubbing? true end def scrub(transcript) transcript. gsub(%r((\\?"cc_number\\?":\\?")[^\\"]+(\\?"))i, '\1[FILTERED]\2'). gsub(%r((\\?"cvv\\?":\\?")[^\\"]+(\\?"))i, '\1[FILTERED]\2'). gsub(%r((Authorization: Bearer )\w+)i, '\1[FILTERED]\2') end private def authorize_with_token(post, money, token, options) add_token(post, token) add_product_data(post, money, options) commit('/checkout/create', post, options) end def add_product_data(post, money, options) post[:account_id] = @options[:account_id] post[:amount] = amount(money) post[:short_description] = (options[:description] || 'Purchase') post[:type] = (options[:type] || 'goods') post[:currency] = (options[:currency] || currency(money)) post[:long_description] = options[:long_description] if options[:long_description] post[:payer_email_message] = options[:payer_email_message] if options[:payer_email_message] post[:payee_email_message] = options[:payee_email_message] if options[:payee_email_message] post[:reference_id] = options[:order_id] if options[:order_id] post[:unique_id] = options[:unique_id] if options[:unique_id] post[:redirect_uri] = options[:redirect_uri] if options[:redirect_uri] post[:callback_uri] = options[:callback_uri] if options[:callback_uri] post[:fallback_uri] = options[:fallback_uri] if options[:fallback_uri] post[:require_shipping] = options[:require_shipping] if options[:require_shipping] post[:shipping_fee] = options[:shipping_fee] if options[:shipping_fee] post[:charge_tax] = options[:charge_tax] if options[:charge_tax] post[:mode] = options[:mode] if options[:mode] post[:preapproval_id] = options[:preapproval_id] if options[:preapproval_id] post[:prefill_info] = options[:prefill_info] if options[:prefill_info] post[:funding_sources] = options[:funding_sources] if options[:funding_sources] post[:payer_rbits] = options[:payer_rbits] if options[:payer_rbits] post[:transaction_rbits] = options[:transaction_rbits] if options[:transaction_rbits] add_fee(post, options) end def add_token(post, token) payment_method = {} payment_method[:type] = 'credit_card' payment_method[:credit_card] = { id: token, auto_capture: false } post[:payment_method] = payment_method end def add_fee(post, options) if options[:application_fee] || options[:fee_payer] post[:fee] = {} post[:fee][:app_fee] = options[:application_fee] if options[:application_fee] post[:fee][:fee_payer] = options[:fee_payer] if options[:fee_payer] end end def parse(response) JSON.parse(response) end def commit(action, params, options={}) begin response = parse( ssl_post( ((test? ? test_url : live_url) + action), params.to_json, headers(options) )) rescue ResponseError => e response = parse(e.response.body) end return Response.new( success_from(response), message_from(response), response, authorization: authorization_from(response, params), test: test? ) rescue JSON::ParserError return unparsable_response(response) end def success_from(response) (!response['error']) end def message_from(response) (response['error'] ? response['error_description'] : 'Success') end def authorization_from(response, params) return response['credit_card_id'].to_s if response['credit_card_id'] original_amount = response['amount'].nil? ? nil : sprintf('%0.02f', response['amount']) [response['checkout_id'], original_amount].join('|') end def split_authorization(authorization) auth, original_amount = authorization.to_s.split('|') [auth, original_amount] end def unparsable_response(raw_response) message = 'Invalid JSON response received from WePay. Please contact WePay support if you continue to receive this message.' message += " (The raw response returned by the API was #{raw_response.inspect})" return Response.new(false, message) end def headers(options) headers = { 'Content-Type' => 'application/json', 'User-Agent' => "ActiveMerchantBindings/#{ActiveMerchant::VERSION}", 'Authorization' => "Bearer #{@options[:access_token]}" } headers['Api-Version'] = options[:version] if options[:version] headers['Client-IP'] = options[:ip] if options[:ip] headers['WePay-Risk-Token'] = options[:risk_token] if options[:risk_token] headers end end end end