// Script for 'engine type: 3' ("GraalJS standard mode")
// PayPal Invoicing API v2 - Create a draft invoice (2026-11-06)
//////// START "main()" /////////////////////////////////////////////////////////////////
main();
function main(){
//// == Config Retrieving / 工程コンフィグの参照 ==
const oauth2 = configs.getObject( "AuthzConfA1" ); // required (Authz by Basic_AuthN)
engine.log( " AutomatedTask Config: Authz Setting: " + oauth2.getName() );
const strTaxsetting = configs.get( "StrConfA2" ) + "";
let boolTaxinclusive = false;
if( strTaxsetting === "true" ){
boolTaxinclusive = true;
}
engine.log( " AutomatedTask Config: Tax Inclusive: " + boolTaxinclusive );
const strCurrencycode = configs.get( "StrConfA3" ) + ""; // required
if( strCurrencycode === "" ){
throw new Error( "\n AutomatedTask ConfigError:" +
" Config {A3 Currency Code} not specified \n" );
}
const strInvnumInvdue = configs.get( "StrConfA4" ) + "";
let strInvnumber = "";
let strInvdue = "";
const arrInvnumInvdue = strInvnumInvdue.split("\n");
if( arrInvnumInvdue.length >= 1 ){
strInvnumber = arrInvnumInvdue[0];
}
if( arrInvnumInvdue.length >= 2 ){
strInvdue = arrInvnumInvdue[1];
}
engine.log( " AutomatedTask Config: Invoice Number: " + strInvnumber );
engine.log( " AutomatedTask Config: Invoice Due: " + strInvdue );
const strInvdate = configs.get( "StrConfA5" ) + "";
engine.log( " AutomatedTask Config: Invoice Date: " + strInvdate );
const strInvoicerEmail = configs.get( "StrConfB1" ) + ""; // required
const strInvoicerinfo = configs.get( "StrConfB2" ) + "";
let strInvoicerBizname = "";
let strInvoicerName1 = "";
let strInvoicerName2 = "";
let strInvoicerAddit = "";
const arrInvoicerinfo = strInvoicerinfo.split("\n");
if( arrInvoicerinfo.length >= 1 ){
strInvoicerBizname = arrInvoicerinfo[0];
}
if( arrInvoicerinfo.length >= 2 ){
strInvoicerName1 = arrInvoicerinfo[1];
}
if( arrInvoicerinfo.length >= 3 ){
strInvoicerName2 = arrInvoicerinfo[2];
}
if( arrInvoicerinfo.length >= 4 ){
strInvoicerAddit = arrInvoicerinfo[3];
}
const strInvoicerProfile = configs.get( "StrConfB3" ) + "";
let strInvoicerLogo = "";
let strInvoicerWeb = "";
let strInvoicerTaxid = "";
const arrInvoicerProfile = strInvoicerProfile.split("\n");
if( arrInvoicerProfile.length >= 1 ){
strInvoicerLogo = arrInvoicerProfile[0];
}
if( arrInvoicerProfile.length >= 2 ){
strInvoicerWeb = arrInvoicerProfile[1];
}
if( arrInvoicerProfile.length >= 3 ){
strInvoicerTaxid = arrInvoicerProfile[2];
}
const strRecipientEmail = configs.get( "StrConfC1" ) + ""; // required
if( strRecipientEmail === "" ){
throw new Error( "\n AutomatedTask ConfigError:" +
" Config {C1 Recipient Email} not specified \n" );
}
engine.log( " AutomatedTask Config: Recipient Email: " + strRecipientEmail );
const strRecipientLang = configs.get( "StrConfC2" ) + "";
engine.log( " AutomatedTask Config: Recipient Lang: " + strRecipientLang );
const strRecipientinfo = configs.get( "StrConfC3" ) + "";
let strRecipientBizname = "";
let strRecipientName1 = "";
let strRecipientName2 = "";
const arrRecipientinfo = strRecipientinfo.split("\n");
if( arrRecipientinfo.length >= 1 ){
strRecipientBizname = arrRecipientinfo[0];
}
if( arrRecipientinfo.length >= 2 ){
strRecipientName1 = arrRecipientinfo[1];
}
if( arrRecipientinfo.length >= 3 ){
strRecipientName2 = arrRecipientinfo[2];
}
engine.log( " AutomatedTask Config: Recipient BizName: " + strRecipientBizname );
engine.log( " AutomatedTask Config: Recipient Name1: " + strRecipientName1 );
engine.log( " AutomatedTask Config: Recipient Name2: " + strRecipientName2 );
const strAdditionalRecipients = configs.get( "StrConfD" ) + "";
const arrAdditionalRecipients = strAdditionalRecipients.split(/\r?\n/).map(line => line.trim()).filter(line => line !== "");
if( strAdditionalRecipients === "" ){
engine.log( " AutomatedTask Config: # of Additional Recipients: 0" );
}else{
engine.log( " AutomatedTask Config: # of Additional Recipients: " + arrAdditionalRecipients.length );
}
const strTsvItems = configs.get( "StrConfE" ) + "";
if( strTsvItems === "" ){
throw new Error( "\n AutomatedTask ConfigError:" +
" Config {E TSV for Items} not specified \n" );
}
const arrTsvItems = strTsvItems.split(/\r?\n/).filter(line => line.trim() !== "");
engine.log( " AutomatedTask Config: # of Items: " + arrTsvItems.length );
if (arrTsvItems.length === 0) {throw new Error("商品明細がありません");}
const strNoteToRecipient = configs.get( "StrConfF" ) + "";
const strTermsConditions = configs.get( "StrConfG" ) + "";
const strMemo = configs.get( "StrConfH" ) + "";
const strPocketInvoiceid = configs.getObject( "SelectConfI" );
const strPocketInvoiceurl = configs.getObject( "SelectConfJ" );
const strPocketInvoiceStatus = configs.getObject( "SelectConfK" );
//// == Data Retrieving / ワークフローデータの参照 ==
// (Nothing. Retrieved via Expression Language in Config Retrieving)
//// == Calculating / 演算 ==
// prepare request1: Get an Access Token
// (PayPal OAuth 2.0 credentials / Client Credentials)
// https://developer.paypal.com/docs/api/overview#get-credentials
// https://developer.paypal.com/docs/api/get-an-access-token-curl/
let postUri1 = "https://api-m.paypal.com/v1/oauth2/token";
let request1 = httpClient.begin(); // HttpRequestWrapper
request1 = request1.authSetting( oauth2 ); // with "Authorization: Basic "
request1 = request1.formParam( "grant_type", "client_credentials" );
engine.log( " AutomatedTask ApiRequest1 Prepared" );
// RFC 6749 - OAuth 2.0 - Client Credentials Grant
// - https://tools.ietf.org/html/rfc6749#section-4.4.2
// try request1
const response1 = request1.post( postUri1 ); // HttpResponseWrapper
engine.log( " AutomatedTask ApiRequest1 Start: " + postUri1 );
const response1Code = response1.getStatusCode() + ""; // (primitive string)
const response1Body = response1.getResponseAsString() + "";
engine.log( " AutomatedTask ApiResponse1 Status: " + response1Code );
if( response1Code !== "200"){
throw new Error( "\n AutomatedTask UnexpectedResponseError: " +
response1Code + "\n" + response1Body + "\n" );
}
// parse response1
/*** engine.log( response1Body ); // debug
response sample
{
"scope":"https://api.paypal.com/v1/payments/.※ https://uri.paypal.com/services/invoicing
openid https://uri.paypal.com/payments/payouts ....",
"access_token":"A21AAO9wpfXXXXXXXXXXL1P96HvZkRXXXXXXXXXXDlSVGSvUjlXXXXX...XXXXX",
"token_type":"Bearer",
"app_id":"APP-6BKXXX232XXX3090N",
"expires_in":32400,
"nonce":"2021-01-14T06:13:03Z1dgXXXCnnXXXgLKXXXgn0XXXcrJXXXoALXXXXBUXXXs"
}
***/
const response1Obj = JSON.parse( response1Body );
const strBearerToken = response1Obj.access_token;
// prepare request2: Create Invoice Draft in PayPal.com
let request2Obj = {};
request2Obj.detail = {}; // "detail" object required in PayPal API
if( strInvnumber !== "" ){
request2Obj.detail.invoice_number = strInvnumber;
}
if( strInvdate !== "" ){
request2Obj.detail.invoice_date = strInvdate;
}
request2Obj.detail.currency_code = strCurrencycode; // required in PayPal API
if( strNoteToRecipient !== "" ){
request2Obj.detail.note = strNoteToRecipient;
}
if( strTermsConditions !== "" ){
request2Obj.detail.terms_and_conditions = strTermsConditions; /// API-Doc may be wrong
}
if( strMemo !== "" ){
request2Obj.detail.memo = strMemo;
}
if( strInvdue !== "" ){
request2Obj.detail.payment_term = {
term_type: "DUE_ON_DATE_SPECIFIED",
due_date: strInvdue
};
}
request2Obj.invoicer = {}; // required in this addon
request2Obj.invoicer.email_address = strInvoicerEmail;
if( strInvoicerBizname !== "" ){
request2Obj.invoicer.business_name = strInvoicerBizname; /// No info in API-Doc
}
request2Obj.invoicer.name = {};
request2Obj.invoicer.name.given_name = strInvoicerName1;
request2Obj.invoicer.name.surname = strInvoicerName2;
if( strInvoicerLogo !== "" ){
request2Obj.invoicer.logo_url = strInvoicerLogo;
}
if( strInvoicerWeb !== "" ){
request2Obj.invoicer.website = strInvoicerWeb;
}
if( strInvoicerTaxid !== "" ){
request2Obj.invoicer.tax_id = strInvoicerTaxid;
}
if( strInvoicerAddit !== "" ){
request2Obj.invoicer.additional_notes = strInvoicerAddit;
}
request2Obj.primary_recipients = []; // required (length must be 1 in this addon)
request2Obj.primary_recipients[0] = {};
request2Obj.primary_recipients[0].billing_info = {};
request2Obj.primary_recipients[0].billing_info.email_address = strRecipientEmail;
if( strRecipientLang !== "" ){
request2Obj.primary_recipients[0].billing_info.language = strRecipientLang;
}
if( strRecipientBizname !== "" ){
request2Obj.primary_recipients[0].billing_info.business_name = strRecipientBizname;
}
request2Obj.primary_recipients[0].billing_info.name = {};
if( strRecipientName1 !== "" ){
request2Obj.primary_recipients[0].billing_info.name.given_name = strRecipientName1;
}
if( strRecipientName2 !== "" ){
request2Obj.primary_recipients[0].billing_info.name.surname = strRecipientName2;
}
if( arrAdditionalRecipients.length > 0 ){
request2Obj.additional_recipients = arrAdditionalRecipients;
}
request2Obj.items = []; // "items" array required in PayPal API
for( let i = 0; i < arrTsvItems.length; i++ ){
let arrColumns = arrTsvItems[i].split("\t");
if (
arrColumns.length < 3 ||
arrColumns[0].trim() === "" ||
arrColumns[1].trim() === "" ||
arrColumns[2].trim() === ""
) {
throw new Error(
"TSV " + (i + 1) + "行目: 商品名・数量・単価は必須です"
);
}
request2Obj.items[i] = {};
request2Obj.items[i].name = arrColumns[0]; // required
request2Obj.items[i].quantity = arrColumns[1].replace(/,/g, ""); // required
request2Obj.items[i].unit_amount = {};
request2Obj.items[i].unit_amount.currency_code = strCurrencycode;
request2Obj.items[i].unit_amount.value = arrColumns[2].replace(/,/g, ""); // required
if( arrColumns.length >= 4 ){
request2Obj.items[i].description = arrColumns[3];
}
if( arrColumns.length >= 6 ){
if( arrColumns[4] !== "" && arrColumns[5] !== "" ){
request2Obj.items[i].tax = {};
request2Obj.items[i].tax.name = arrColumns[4];
request2Obj.items[i].tax.percent = arrColumns[5];
}
}
if( arrColumns.length >= 7 ){
if( arrColumns[6] !== "" ){
request2Obj.items[i].discount = {};
request2Obj.items[i].discount.amount = {};
request2Obj.items[i].discount.amount.currency_code = strCurrencycode;
request2Obj.items[i].discount.amount.value = arrColumns[6].replace(/,/g, "");
}
}
if( arrColumns.length >= 8 ){
if( arrColumns[6] !== "" && arrColumns[7] !== "" ){
throw new Error( "\n AutomatedTask UnexpectedTsvError:" +
" Cannot discount by both Amount and Percent: " + i + "\n" );
}
if( arrColumns[7] !== "" ){
request2Obj.items[i].discount = {};
request2Obj.items[i].discount.percent = arrColumns[7];
}
}
}
request2Obj.configuration = {};
request2Obj.configuration.tax_inclusive = boolTaxinclusive;
let postUri2 = "https://api-m.paypal.com/v2/invoicing/invoices";
let request2 = httpClient.begin(); // HttpRequestWrapper
request2 = request2.bearer( strBearerToken );
request2 = request2.body( JSON.stringify( request2Obj ), "application/json" );
engine.log( " AutomatedTask ApiRequest2 Prepared" );
// try request2
const response2 = request2.post( postUri2 ); // HttpResponseWrapper
engine.log( " AutomatedTask ApiRequest2 Start: " + postUri2 );
const response2Code = response2.getStatusCode() + ""; // (primitive string)
const response2Body = response2.getResponseAsString() + "";
engine.log( " AutomatedTask ApiResponse2 Status: " + response2Code );
if( response2Code !== "201"){
throw new Error( "\n AutomatedTask UnexpectedResponseError: " +
response2Code + "\n" + response2Body + "\n" );
}
// https://developer.paypal.com/docs/api-basics/HTMLStatusCodes/
// parse response2
/*** engine.log( response2Body ); // debug
response sample
{
"rel":"self",
"href":"https://api.paypal.com/v2/invoicing/invoices/INV2-XXXX-8H2Z-XXXX-YGSU",
"method":"GET"
}
***/
const response2Obj = JSON.parse( response2Body );
// prepare request3: Show invoice details
const selfLink = (response2Obj.links || [])
.find(link => link.rel === "self");
let getUri3 = response2Obj.href
|| (selfLink && selfLink.href)
|| (response2Obj.id
? postUri2 + "/" + encodeURIComponent(response2Obj.id)
: null);
if (!getUri3) {
throw new Error("請求書作成レスポンスから詳細取得URLを取得できません");
}
let request3 = httpClient.begin(); // HttpRequestWrapper
request3 = request3.bearer( strBearerToken );
engine.log( " AutomatedTask ApiRequest3 Prepared" );
// try request3
const response3 = request3.get( getUri3 ); // HttpResponseWrapper
engine.log( " AutomatedTask ApiRequest3 Start: " + getUri3 );
const response3Code = response3.getStatusCode() + ""; // (primitive string)
const response3Body = response3.getResponseAsString() + "";
engine.log( " AutomatedTask ApiResponse3 Status: " + response3Code );
if( response3Code !== "200"){
throw new Error( "\n AutomatedTask UnexpectedResponseError: " +
response3Code + "\n" + response3Body + "\n" );
}
// parse response3
/*** engine.log( response3Body ); // debug
response sample
{
"id":"INV2-XXXX-ZDDH-XXXX-RAJ4",
"status":"DRAFT",
"detail":{
"currency_code":"JPY",
"note":"“Questetra BPM Suite” は、....",
"memo":"BPM 1361963",
"additional_data":"6000835 京都市中京区高宮町204御池ビル4階",
"category_code":"SHIPPABLE",
"invoice_number":"20210118-1361963",
"invoice_date":"2021-01-17",
"payment_term":{
"term_type":"DUE_ON_DATE_SPECIFIED",
"due_date":"2021-04-30"
},
"viewed_by_recipient":false,
"group_draft":false,
"metadata":{
"create_time":"2021-01-18T07:00:03Z",
"last_update_time":"2021-01-18T07:00:03Z",
"created_by_flow":"REGULAR_SINGLE",
"recipient_view_url":"https://www.paypal.com/invoice/p/#XXXXZDDHXXXXRAJ4",
"invoicer_view_url":"https://www.paypal.com/invoice/details/INV2-XXXX-ZDDH-XXXX-RAJ4",
"caller_type":"API_V2_INVOICE"
},
"archived":false
},
"invoicer":{ ◆ },
"primary_recipients":[{ ◆ }],
"additional_recipients":[ "example1@example.net", "example2@example.com" ],
"items":[{ ◆ },{ ◆ },{ ◆ }],
"configuration":{
"tax_calculated_after_discount":false,
"tax_inclusive":false,
"allow_tip":false,
"template_id":"TEMP-137273103M373943N"
},
"amount":{ ◆ },
"due_amount":{ ◆ },
}
***/
const response3Obj = JSON.parse( response3Body );
let strInvoiceid = response3Obj.id;
let strInvoiceurl = response3Obj.detail?.metadata?.invoicer_view_url ?? "";
let strInvoiceStatus = response3Obj.status;
//// == Data Updating / ワークフローデータへの代入 ==
if( strPocketInvoiceid !== null ){
engine.setData( strPocketInvoiceid, strInvoiceid );
}
if( strPocketInvoiceurl !== null ){
engine.setData( strPocketInvoiceurl, strInvoiceurl );
}
if( strPocketInvoiceStatus !== null ){
engine.setData( strPocketInvoiceStatus, strInvoiceStatus );
}
} //////// END "main()" /////////////////////////////////////////////////////////////////