issue を見るとなんかありそう
- invoice
- payment
- address
- customer_data
credentialが追加されてる
サンプル込のやつができてる。テンプレートは generators/gateway/templates/gateway.rb
self.test_url = 'https://beta.test.jp/cgi-bin/order'
self.live_url = 'https://example.com/live'
self.supported_countries = ['JP']
self.default_currency = 'JPY'
# Webpayのを見て足したんだけど、あとで見る
self.money_format = :cents
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
# ここから下はどこで使われるだろう
self.homepage_url = 'http://www.example.net/'
self.display_name = 'New Gateway'
def initialize(options={})
requires!(options, :contact_code)
super
end
lib/active_merchant/billing/gateway.rb|222| def requires!(hash, *params)
params.each do |param|
if param.is_a?(Array)
raise ArgumentError.new("Missing required parameter: #{param.first}") unless hash.has_key?(param.first)
valid_options = param[1..-1] # 先頭以外
raise ArgumentError.new("Parameter: #{param.first} must be one of #{valid_options.to_sentence(:words_connector => 'or')}") unless valid_options.include?(hash[param.first])
else
raise ArgumentError.new("Missing required parameter: #{param}") unless hash.has_key?(param)
end
end
arrayのarrayも受け付けている?
> requires!({contact_code: 4}, [:contact_code, 1, 2, 3]) # => ArgumentError
@optionsしているだけ
def initialize(options = {})
@options = options
end
publicなインタフェースとかオプションとか
lib/active_merchant/billing/gateway.rb のrdoc
# * <tt>purchase(money, credit_card, options = {})</tt>
# * <tt>authorize(money, credit_card, options = {})</tt>
# * <tt>capture(money, authorization, options = {})</tt>
# * <tt>void(identification, options = {})</tt>
# * <tt>refund(money, identification, options = {})</tt>
# * <tt>verify(credit_card, options = {})</tt>
#
# Some gateways also support features for storing credit cards:
#
# * <tt>store(credit_card, options = {})</tt>
# * <tt>unstore(identification, options = {})</tt>
これ以外もわりと自由に定義しているのも結構ある。
実際の購入処理。
第二引数は payment_method
になっているのも沢山ある
def purchase(...)
post = {}
add_invoice(post, money, options)
add_payment(post, payment)
add_address(post, payment, options)
add_customer_data(post, options)
commit('sale', post)
add_xxx みたいなのは全部テンプレートの中にある。
def add_invoice(post, options)
post[:invoiceReference] = options[:order_id] || options[:invoice]
post[:invoiceDescription] = options[:description]
end
ようはハッシュに詰めてcommit。
def commit(action, parameters)
url = (test? ? test_url : live_url)
response = parse(ssl_post(url, post_data(action, parameters)))
Response.new(
success_from(response),
message_from(response),
response,
authorization: authorization_from(response),
test: test?
)
end
ssl_postにはURLと何かを渡す。
include PostsData
shopify/active_utils にあった。
superclass_delegating_accessor ってなんだ?ActiveSupport! どこに差し込まれるんだろう (core_ext/class/deligating_attributes)
ActiveMerchant から切り出しただけだったけど、ActiveUtilsにちゃんとやるわーのPR Shopify/active_utils#36
ssl_post
def ssl_get(endpoint, headers={})
ssl_request(:get, endpoint, nil, headers)
end
def ssl_post(endpoint, data, headers = {})
ssl_request(:post, endpoint, data, headers)
end
def ssl_request(method, endpoint, data, headers)
handle_response(raw_ssl_request(method, endpoint, data, headers))
end
def raw_ssl_request(method, endpoint, data, headers = {})
logger.warn "#{self.class} using ssl_strict=false, which is insecure" if logger unless ssl_strict
...
connection.request(method, data, headers)
end
private
def handle_response(response)
case response.code.to_i
when 200...300
response.body
else
raise ResponseError.new(response)
end
end
最終的には connection.requrest
にどう渡すか。SSL通信がめんどくさい?
Connection、Benchmarkで実行時間をとって、infoログにだしてる!
ssl_postの第二引数にはHTTP bodyをあげる。もしendpointが変わるようなら、urlの方を変えないといけない。
parse には何が来る? Net::HTTP#postの返り値。handle_responseで200番台以外は例外になっている
Net::HTTP::Response#body みたいなやつがきそう。(handle_responseの中)
Response.new(
success_from(response),
message_from(response),
response,
authorization: authorization_from(response),
test: test?
)
lib/active_merchant/billing/response.rb
class Response
attr_reader :params, :message, :test, :authorization, :avs_result, :cvv_result
def success?
@success
end
def test?
@test
end
def fraud_review?
@fraud_review
end
def initialize(success, message, params = {}, options = {})
@success, @message, @params = success, message, params.stringify_keys
@test = options[:test] || false
@authorization = options[:authorization]
@fraud_review = options[:fraud_review]
@avs_result = if options[:avs_result].kind_of?(AVSResult)
options[:avs_result].to_hash
else
AVSResult.new(options[:avs_result]).to_hash
end
@cvv_result = if options[:cvv_result].kind_of?(CVVResult)
options[:cvv_result].to_hash
else
CVVResult.new(options[:cvv_result]).to_hash
end
end
end
authorization, fraund_review, AVSResultとCVVResultとは?
この金額を引き落とせますかー?の確認。
authorizeに対して実際に引き落とす処理。
purchase = authorize + capture
https://github.com/Shopify/active_merchant/wiki/GatewaysVsIntegrations に説明がある。
トランザクションを無効にする(返金との違いは?)
返金
クレジットカードの有効性だけを確認する。
少額のauthorizeをしてからすぐvoidしている。
クレジットカード登録
クレジットカードの登録解除
verifyの中などではよく使われている。
MultiResponse.run(:use_first_response) do |r|
r.process { authorize(100, credit_card, options) }
r.process(:ignore_result) { void(r.authorization, options) }
end
class MultiResponse < Response
def self.run(use_first_response = false, &block)
new(use_first_response).tap(&block)
end
def initialize(use_first_response = false)
@responses = []
@use_first_response = use_first_response
@primary_response = nil
end
end
runの第一引数はbooleanなんだけど…
なるほど、nilとfalse以外はtrueを巧妙につかって表現していると理解した。
普通に使うと、
- use_first_response => false
- ignore_result => false
最後に成功したRequestがprimary_requestになる
エラーが突き抜けちゃうけどいいのかなぁ
bogusっていうテスト用のブランドも用意しているよ
creadit_card_method というのにブランド毎のカード番号バリデーションが入っている。
creadit_card_formatting は年月を指定したフォーマットに変換する