api
schedule
omise
定期課金

Omise Schedule API 課金が失敗したときのフロー

More than 1 year has passed since last update.

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 になっています。

ちなみに、ダッシュボード上だと下記のようになります。

Screen Shot 2018-03-07 at 14.48.19.png

課金のリトライが最後まで失敗した場合、スケジュールのステータスは、suspendedとなります。


まとめ

Schedule APIの課金は三回リトライされますが、それでもダメだった場合は、顧客にカード情報を更新してもらい、それで再度手動で課金する、ないしスケジュールを作り直す、もしくは顧客との契約をやめる....という選択肢がありました。

わからないことなどありましたら遠慮なくコメントしてください。