請求の自動化
ワークフローシステム『Questetra』から、請求システムとしての『PayPal』をコントロールできれば、とても便利です。たとえば『商品納入フロー』が完了した直後に「納入内容に合わせた PayPal 請求書」が自動発行されるような仕組みを作れば、「請求ミス」や「請求モレ」の発生を防ぐことができます。もし、現状が「紙の請求書」を郵送する請求業務なのであれば、切手代・印刷費・事務人件費を全てゼロにすることも可能です。
前回までの記事では、「PayPal 内に請求書を生成させる」や「PayPal 内に生成させた請求書を更にお客様にメール送信させる」といった指示が、PayPal に対して自動的に行われる仕組みを紹介しました。
更なる自動化
- PayPal に「PayPal請求書」を生成させる
- PayPal に「PayPal請求書」をメール送信させる
- PayPal に対して、入金があったか確認する
今回の記事では、自動工程を上手に組み立てて『入金確認フロー』を無人化することを考察してみます。
※ここでは自動工程の一種である「スクリプト工程」を使うため、プログラミング知識が必要となります
[エントリ受付システム-ペイパル請求~入金確認]
[エントリ受付システム-ペイパル請求~入金確認:「2.PayPal決済確認」画面]
決済ステータスの自動確認
この例で配置されているスクリプト工程『入金チェック』では、案件が到達する度に、「Show invoice details」がリクエストされます。- GET "https://api.paypal.com/v1/invoicing/invoices/" + paypalId
その際に得られる「PayPal からのレスポンス」が自動的に参照され、すでに支払いが行われているかどうか(PayPal請求書のステータス)を検知できる仕組みとなっています。
もし支払いが行われていると判明した場合には「領収書発行日セット」「領収書PDF生成」「領収書PDFメール送信」の工程に自動的に流れていきます。
また、もし支払いが行われていない場合には、人間工程「2.PayPal決済確認」に戻ってきますが、この工程は「24時間放置されると改めて下流に流れる」という設定になっているため、更に24時間後に改めて「PayPal請求書」のステータス確認が行われることになります。(この例では手動で流すこともルートも用意されています)
※ 他にも「支払完了通知メール」や「Webhook」によるステータス確認方法も考えられます。
サンプル・コード
スクリプト工程『入金チェック』にセットすべきスクリプトのサンプルを、以下に掲示します。※ 上流にあるスクリプト工程『請求書生成』と『請求書送信』については、前回までの記事を参照してください。
ポイントは、レスポンスされた JSON オブジェクトのステータス「ResponseObj.status」が『PAID』になっている場合に、『ResponseObj.payments[0].date』の値を案件データとして取り込んでいるところです。分岐ポイントでは、「入金日」のデータが取り込まれた案件について「すでに支払いが行われている」と認識するように設定されています。
更に他のデータを取り込む必要がある場合には、もう少し実装項目を増やす必要があります。その場合、以下に「レスポンスされた JSON オブジェクト」の SAMPLE RESPONCE をあわせて掲示しておきますので、必要に応じて参照して、適宜スクリプトを加筆してください。
<スクリプト工程「入金チェック」の設定例>
// PayPal Invoicing API SEND (ver. 20170322)
// (c) 2017, Questetra, Inc. (the MIT License)
var sandboxmode = false; // true or false
//// == コンフィグ&参照 / Config & Retrieving ==
// Your "REST API app" on PayPal Dashboard
// https://developer.paypal.com/developer/applications/
var clientId = "Your-Client-Id-Your-Client-Id-Your-Client-Id-Your-Client-Id-Your-Client-Id-Your-";
var secret = "Your-Secret-Your-Secret-Your-Secret-Your-Secret-Your-Secret-Your-Secret-Your-Sec";
var clientIdSandbox = "Your-Client-Id-Your-Client-Id-Your-Client-Id-Your-Client-Id-Your-Client-Id-Your-";
var secretSandbox = "Your-Secret-Your-Secret-Your-Secret-Your-Secret-Your-Secret-Your-Secret-Your-Sec";
if( sandboxmode ){ clientId = clientIdSandbox; secret = secretSandbox; }
var paypalId = data.get( "q_paypalId" ) + "";
//// == 演算 / Calculating ==
var accessLog = "";
// Getting OAuth2 Token via client_credentials grant
var uriToken = "https://api.paypal.com/v1/oauth2/token";
var uriTokenSandbox = "https://api.sandbox.paypal.com/v1/oauth2/token";
if( sandboxmode ){ uriToken = uriTokenSandbox; }
var response = httpClient.begin()
.basic( clientId, secret )
.formParam( "grant_type", "client_credentials" )
.post( uriToken );
accessLog += "---POST request--- " + response.getStatusCode() + "\n";
accessLog += response.getResponseAsString() + "\n";
accessLog += "\n";
var oauthTokenObj = JSON.parse( response.getResponseAsString() );
var oauthToken = oauthTokenObj.access_token;
accessLog += "oauthToken: " + oauthToken + "\n";
accessLog += "\n";
// invoice Detail
var uriDetail = "https://api.paypal.com/v1/invoicing/invoices/" + paypalId;
var uriDetailSandbox = "https://api.sandbox.paypal.com/v1/invoicing/invoices/" + paypalId;
if( sandboxmode ){ uriDetail = uriDetailSandbox; }
var responseDetail = httpClient.begin()
.bearer( oauthToken )
.get( uriDetail );
accessLog += "---GET request--- " + responseDetail.getStatusCode() + "\n";
accessLog += responseDetail.getResponseAsString() + "\n";
accessLog += "\n";
var paypalInvoiceObj = JSON.parse( responseDetail.getResponseAsString() );
var paypalInvoiceStatus = paypalInvoiceObj.status;
var paypalInvoicePaidDate = "";
var paypalInvoicePaidAmount = 0;
if ( paypalInvoiceStatus == "PAID" ){
paypalInvoicePaidDate = paypalInvoiceObj.payments[0].date;
// e.g.: "2017-03-21 22:10:51 PDT"
paypalInvoicePaidDate = paypalInvoicePaidDate.slice(0,10);
paypalInvoicePaidAmount = parseInt( paypalInvoiceObj.payments[0].amount.value );
}
accessLog += "paypalInvoiceStatus: " + paypalInvoiceStatus + "\n";
accessLog += "paypalInvoicePaidDate: " + paypalInvoicePaidDate + "\n";
accessLog += "\n";
//// == ワークフローデータへの代入 / Data Updating ==
retVal.put( "q_accessLog3", accessLog );
retVal.put( "q_paypalInvoicePaidDate", java.sql.Date.valueOf( paypalInvoicePaidDate ) );
retVal.put( "q_paypalInvoicePaidAmount", java.math.BigDecimal( paypalInvoicePaidAmount ) );
<SAMPLE RESPONCE> (※部分的に編集されています)
※ GET "https://api.paypal.com/v1/invoicing/invoices/" + paypalId
{
"id":"INV2-XXXX-YYYY-ZZZZ-FXC6",
"number":"3410",
"template_id":"TEMP-52B5XXXXSA83YYYY3",
"status":"PAID",
"merchant_info":{
"email":"account+paypal@example.com",
"business_name":"京大情報学同窓会",
"website":"http://www.example.com/",
"address":{
"line1":"左京区吉田本町",
"line2":"京都大学",
"city":"京都市",
"state":"京都府",
"postal_code":"606-8501",
"country_code":"JP"
}
},
"billing_info":[
{
"email":"jy@yambal.net",
"first_name":"テスト 一郎 様",
"business_name":"テスト部",
"language":"ja_JP"
}
],
"items":[
{
"name":"一般社会人(3000円)",
"quantity":1.0,
"unit_price":{
"currency":"JPY",
"value":"3000.00"
}
}
],
"invoice_date":"2017-03-21 PDT",
"payment_term":{
"term_type":"NET_10",
"due_date":"2017-03-31 PDT"
},
"tax_calculated_after_discount":false,
"tax_inclusive":false,
"merchant_memo":"https://YourWorkflowPlatformId.questetra.net/PE/Workitem/list?processInstanceId=3410",
"total_amount":{
"currency":"JPY",
"value":"3000.00"
},
"payments":[
{
"type":"PAYPAL",
"transaction_id":"0335XXXX9112YYYYL",
"transaction_type":"SALE",
"method":"PAYPAL",
"date":"2017-03-21 22:10:51 PDT",
"amount":{
"currency":"JPY",
"value":"3000.00"
}
}
],
"metadata":{
"created_date":"2017-03-21 22:02:49 PDT",
"last_updated_date":"2017-03-21 22:10:51 PDT",
"first_sent_date":"2017-03-21 22:08:04 PDT",
"last_sent_date":"2017-03-21 22:08:04 PDT"
},
"paid_amount":{
"paypal":{
"currency":"JPY",
"value":"3000.00"
}
},
"allow_tip":false,
"links":[
{
"rel":"self",
"href":"https://api.paypal.com/v1/invoicing/invoices/INV2-XXXX-YYYY-ZZZZ-FXC6",
"method":"GET"
}
]
}
<データ項目一覧画面>
[雛形ダウンロード (無料)]
- 業務テンプレート:エントリ受付システム-ペイパル請求~入金確認
- 第504話:監査書類を Dropbox に自動保存! (2016-10-11)
- 第503話:ユラギを無くすには「マスター参照」でしょ!(kintone編) (2016-10-03)
- 第498話:自動工程におけるデータ加工、アレコレ(その5) (2016-08-29)
- M230 自動工程: 業務データの複雑なデータ加工が自動実行されるように設定する(ECMAスクリプト) (使い方)
- M415 自動工程: 業務プロセス定義で利用可能な自動工程を追加する (使い方)
- M416 自動工程: 業務プロセス定義で利用可能な自動工程を自作する (使い方)
- M202 業務の流れ: 処理フローを定義し、各工程の締切時刻を設定する (使い方)
[英文記事 (English Entry) ]





ヒューマン工程の締切タイムアウトは、処理時刻として保存されない?
返信削除