Help us understand the problem. What is going on with this article?

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

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

omise
オンライン決済サービスを提供している企業です
https://www.omise.co/ja
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away