請求の自動化
ワークフローシステム『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) ]
ヒューマン工程の締切タイムアウトは、処理時刻として保存されない?
返信削除