OmiseのSchedule APIは、仮に課金が失敗した場合、三回までリトライをしてくれます。
課金が失敗した場合、というのは、例えば課金対象のカードが使用限度額を超えているとか、カード自体が盗まれたカードとか....諸々の理由です。
この場合、2回リトライしてダメだった場合、Scheduleは停止します。
この後の対処ですが、以下のような選択肢があると考えられます。
1. 該当の顧客のカード情報を更新して、課金を手動でやる
2. 該当の顧客のカード情報を更新して、スケジュールを作り直す
3. 顧客へのサービスを停止する
該当の顧客カードの更新
課金が失敗したら、その顧客カードはダメダメですから、ダメダメじゃないカードに差し替えないといけません。
ダッシュボードを見て確認して、該当の顧客カード情報を更新する方法ももちろんあります。
また、SearchAPIというものを使って条件検索し、必要なものをとってくる方法もあります。
curl -s 'https://api.omise.co/search?scope=charge&filters\[failure_code\]=insufficient_fund' -u skey_moominmoomimoomiqijwxm04jmoomootit: | grep "cust_"
また、以下のようなスクリプトを使ったりもできます
#!/usr/bin/env ruby
require "json"
#
# the intension of this script is as follows:
#
#
# 1. collect all failed cards, using search API
# 2. charge those cards again
#
skey = ARGV[0]
if /^skey_/ !~ skey
abort "SKEY #{skey} is invalid."
end
def get_failed_charges(skey)
command = "curl -s 'https://api.omise.co/search" \
"?scope=charge&filters\\" \
"[failure_code\\]=insufficient_fund' " \
"-u #{skey}:"
`#{command}`
end
def get_bad_cards(skey)
bad_cards = JSON
.parse(get_failed_charges(skey))["data"]
.map { |x|
location = x["card"]["location"]
[
/cust_[^\/]+/.match(location).to_s,
/card_[^\/]+/.match(location).to_s,
x["amount"],
x["created"],
]
}
.select{ |x| x[0] != "" && x[1] != "" }
.uniq
end
result = ""
get_bad_cards(skey).each do |customer, old_card, amount, created|
command = "curl https://api.omise.co/charges \\\n " \
" -X POST \\\n " \
" -u #{skey}: \\\n " \
" -d 'amount=#{amount}' \\\n " \
" -d 'currency=jpy' \\\n " \
" -d 'card=[NEW TOKEN OF CUSTOMER #{customer}]'"
result = [
"CREATED: #{created}",
"OLD CARD WAS: #{old_card}",
"YOU NEED TO GET TOKEN OF CUSTOMER: #{customer}\n\n",
command,
].join("\n")
end
def write_result_to_console(result)
tmp = ".tmpfileforjson"
File.open(tmp, "w") do |f|
f.puts result
end
system("vim #{tmp}")
end
write_result_to_console(result)
そうするとvim編集モードでこんな感じで取れます:
CREATED: 2018-03-03T01:21:24Z
OLD CARD WAS: card_test_5b4w0kuostyminyxwnk
YOU NEED TO GET TOKEN OF CUSTOMER: cust_test_5b4w0loxfeq7moorjo6
curl https://api.omise.co/charges \
-X POST \
-u skey_test_5b3moo04jminhtit: \
-d 'amount=100' \
-d 'currency=jpy' \
-d 'card=[NEW TOKEN OF CUSTOMER cust_test_5b4w0loxfeq7qi5rjo6]'
そして、
YOU NEED TO GET TOKEN OF CUSTOMER:
cust_test_5b4w0loxfeq7moorjo6
とあるので、この対象の顧客にOmise.jsを経由してカードの情報を新規に追加、更新してもらう必要があります。
$ omi customer get -v
$ curl -s https://api.omise.co/customers/cust_test_5b4vkgkeal49aafdo2k \
-u skey_test_5moominmoominmoohtit:
そうすると、レスポンスオブジェクトの中に
"cards": {
"object": "list",
"from": "1970-01-01T00:00:00Z",
"to": "2018-03-04T12:04:50Z",
"offset": 0,
"limit": 20,
"total": 1,
"order": null,
"location": "/customers/cust_test_5b4vkgkemoominfdo2k/cards",
"data": [
{
"object": "card",
"id": "card_test_5bmoomq3dmxe4qa3d3",
"livemode": false,
"location": "/customers/cust_test_5b4vmoominl49aafdo2k/cards/card_test_5b4vkmoominmoomin3d3",
"country": "us",
"city": "Bangkok",
"postal_code": "10320",
"financing": "",
"bank": "JPMORGAN CHASE BANK, N.A.",
"last_digits": "0011",
"brand": "Visa",
"expiration_month": 10,
"expiration_year": 2018,
"fingerprint": "ehfxeEmZu9ZOEmZJEh9VdmoominmooADRaITib2A=",
"name": "Insufficient Fund",
"security_code_check": true,
"created": "2018-03-02T04:56:17Z"
}
]
こんな風にかえってくるので、
card_test_5bmoomq3dmxe4qa3d3
これを使うんだな、となります。
手動で課金しなおす場合
先ほどvim編集モードで見た奴の[NEW TOKEN OF CUSTOMER ...]のところに上記card_...
を代入して、
curl https://api.omise.co/charges \
-X POST \
-u skey_test_5b3qijmoomindayotit: \
-d 'amount=100' \
-d 'currency=jpy' \
-d 'card=card_test_5bmoomq3dmxe4qa3d3'
これで手動課金ができます!
スケジュールを作り直す場合
スケジュールの作り方の基本はこちらに譲りますが、例えば日ベースでの課金でやり直す場合、さっき手に入れた cust_..
と card_..
を代入して、
curl -s https://api.omise.co/schedules \
-X POST \
-u skey_test_5moomindayoc0htit: \
-d "every=1" \
-d "period=day" \
-d "start_date=2018-03-04" \
-d "end_date=2118-02-08" \
-d "charge[customer]=cust_test_5b4w0loxfeq7moorjo6" \
-d "charge[card]=card_test_5bmoomq3dmxe4qa3d3" \
-d "charge[amount]=100" \
-d "charge[description]=Membership fee"
こんな感じで実行することになると思います。
課金失敗の通知
webhookとかどうなるの問題ですが、もしダメダメカードの顧客に課金をしようとして失敗した場合、webhookは、charge.create
を投げてきます。その中のステータスが failed
になっています。
課金のリトライが最後まで失敗した場合、スケジュールのステータスは、suspended
となります。
まとめ
Schedule APIの課金は三回リトライされますが、それでもダメだった場合は、顧客にカード情報を更新してもらい、それで再度手動で課金する、ないしスケジュールを作り直す、もしくは顧客との契約をやめる....という選択肢がありました。
わからないことなどありましたら遠慮なくコメントしてください。