Episode 525: How to Send OAuth2 Request from Automatic Step

Monday, March 6, 2017

Automation with server side JavaScript

In the last article (*), I mentioned that "automatic Step of Questetra BPM Suite can implement (not only simple numerical calculation) OAuth2 communication".
* Episode 524: Automation of Work with Server-side JavaScript

Therefore, in this article, I would like to briefly summarize about "what kind of external service is specifically possible with OAuth2 communication" and "what kind of programming is required specifically". (In the viewpoint of requesting from the workflow system side.)

What is OAuth2 communication

OAuth is a "scheme for authorizing requests to servers".

Reference) What is "OAuth", the Cloud Technology which Everyone should Know?

Since OAuth2 first appeared in 2012, APIs of many "resource servers", such as Google Drive and Dropbox, are now compatible with "OAuth2 access". That is, the external system can throw various "Reference request" and "Update request" to "resource server". (RFC6749/6750)

In the cloud-based Workflow "Questetra BPM Suite", it is possible for an automatic Step placed in a business process definition to behave as an "external system" (OAuth2 client). In other words, you can set so that the following requests are automatically executed each time an Issue reaches the automatic Step (Script Task / Service Task).

  • Script Task: a Step in which a Business designer can set server-side JavaScript (Communication using HttpClientWrapper can also be implemented) M230
  • Service Task (Addon): Step which packaged Script Task. Only configure setting is allowed for Business designer. M415 M416

How to get access token

"Access token" is required for such request communication.

"Access token" is "a proof that it is a request authorized by the owner of the resource", and the entity is a string of from about several tens to 200 characters. However, the access token acquisition method differs depending on "resource server".

As main method, there are
  • A. Let the owner click on "grant button" and then automatically obtain. (Authorization Code Grant)
  • B. Set secret credentials, then retrieve automatically (Client Credentials Grant)
  • C. Set the access token itself (e.g. when an Access Token without expiration date is possible)

However, it varies depending on the type or characteristic of data managed by "resource server".

Caution) Even with the OAuth 2 authorization method that the destination API can implement, there might be cases where automatic communication can not be set depending on implementation restrictions (Questetra side) regarding "HTTP header" and "Content - Type format".

[Invoice Submission]

Sending invoice data to PayPal system

In the above workflow, there is an automatic Step(*) at which "Request of automatically creating PayPal invoice" to be sent. (* Script Task: "PayPal invoice Auto-generation")

That is, every time a billing issue arrives at this Step, a request of "create PayPal invoice" is sent to "PayPal system".

Although there is only a Step for "Approval" in the upstream in this example for the sake of simplicity, for actual internal company work, it might be necessary to set up a branching that omits superior's approval for a small amount of billing, or to establish a cooperative Event with the Estimation process. In addition, it is considered that to incorporate "transmission from PayPal system" etc. in the downstream Step.

Sample code

This is a script that sends a request of "Create a invoice draft".

The "PayPal Invoicing API" used here is a pattern of B which uses secret credentials (clientId and secret) and obtains "access token" each time. So, we have obtained credentials from "administrator dashboard" beforehand.

* You must be cautious since "NVP / SOAP API version" (2011 ~) exists besides "REST API version" (2014 ~).

In this script, there is "Getting OAuth2 Token via client_ credentials grant" in the part which starts the operation in the middle section. This will be communication for "Acquiring Access Token". (Line 59 to 62) After that, it goes to a program of request communication for invoice generation using "acquired Access Token".(Line 109 to 102)
  • The first round HTTP communication: "https://api.paypal.com/v1/oauth2/token"
  • The second round HTTP communication: "https://api.paypal.com/v1/invoicing/invoices/"
If I have the chance, I would like to write about "Collaboration that should be between Workflow system and PayPal system" as well. Although it has nothing to do with the topic ..., the rounding for consumption tax in "PayPal invoice" is "half round up". Concerning that, it may be said that "individuality to which you should be prepared in advance" for a Japanese company with a tendency that regards "Floor function" as a virtue.

P.S. (2017-03-08)
Currently we have confirmed several problems with PayPal REST Invoicing API.
(We have already reported about this problem and it has been confirmed by PayPal)
  • The logo image etc. is not reflected in 'invoice' even if template invoice is specified
  • Even if the transmission of telephone number information succeeded, it is not displayed in "invoice"
    (phone.country_code and phone.national_number)
Incidentally, regarding "whatever you designate in the time zone is ignored" (registered as PST), they explain that it is as the "specification" at the moment.

P.S. (2017-06-14)
Telephone number information is reflected by sending it as "child element of address information".(Even though the documents of PayPal is not corrected yet.)
  • invoiceObj.merchant_info.address.phone.country_code = merchantPhoneCC;
  • invoiceObj.merchant_info.address.phone.national_number = merchantPhoneNum;

// PayPal Invoicing (ver. 20170228) 
// (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 = "HogeHoge-SampleSample-YourId-HogeHoge-SampleSample-YourId-HogeHoge-SampleSample-"; 
var secret = "HogeHoge-SampleSample-YourSecret-HogeHoge-SampleSample-YourId-HogeHoge-SampleSam"; 
var clientIdSandbox = "HogeHoge-SampleSample-YourId-HogeHoge-SampleSample-YourId-HogeHoge-SampleSample-"; 
var secretSandbox = "HogeHoge-SampleSample-YourSecret-HogeHoge-SampleSample-YourId-HogeHoge-SampleSam"; 
if( sandboxmode ){ clientId = clientIdSandbox; secret = secretSandbox; } 

// Your Info 
var merchantEmail = "your@example.com"; 
var merchantEmailSandbox = "your-facilitator@example.com"; 
if( sandboxmode ){ merchantEmail = merchantEmailSandbox; } 
var merchantBusinessName = "Example Company, Inc."; 
var merchantAddress1 = "206 Takamiya-cho Nakagyo-ku"; 
var merchantAddress2 = "Oike Bldg. 4th Fl."; 
var merchantAddressCity = "Kyoto-shi"; 
var merchantAddressState = "KYOTO"; 
var merchantAddressPC = "604-0835"; 
var merchantAddressCC = "JP"; 
var merchantWebsite = "https://www.questetra.com/corp/"; 

// Customer Info 
var billingLanguage = "en_US"; 
var billingEmail = data.get( "q_billingEmail" ) + ""; 
var billingName1 = data.get( "q_billingName1" ) + ""; 
var billingName2 = data.get( "q_billingName2" ) + ""; 
var billingBusinessName = data.get( "q_billingBusinessName" ) + ""; 

// Items Info 
var itemUnitPriceCurrency = "JPY"; 
var itemTaxName = "JCT"; 
var itemTaxPercent = 8; 
var itemName = data.get( "q_itemName" ) + ""; 
var itemDescription = data.get( "q_itemDescription" ) + ""; // Max: 1000 
var itemQuantity = data.get( "q_itemQuantity" ) - 0; // number 
var itemUnitPrice = data.get( "q_itemUnitPrice" ) + ""; // string 

// Invoice Info 
var invoiceNumber = processInstance.getProcessInstanceId() + ""; // string 
var invoiceDate = data.get( "q_invoiceDate" ) + " JST"; // yyyy-MM-dd z 
var paymentTermDueDate = data.get( "q_paymentTermDueDate" ) + " JST"; 
var invoiceNote = data.get( "q_invoiceNote" ) + ""; // Max: 4000 
var invoiceTerms = data.get( "q_invoiceTerms" ) + ""; // Max: 4000 

//// == 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"; 
var oauthTokenObj = JSON.parse( response.getResponseAsString() ); 
var oauthToken = oauthTokenObj.access_token; 
accessLog += "oauthToken: " + oauthToken + "\n"; 

// Create invoice 
var invoiceObj = {}; 
invoiceObj.number = invoiceNumber; 
invoiceObj.invoice_date = invoiceDate; 
invoiceObj.payment_term = {}; 
invoiceObj.payment_term.due_date = paymentTermDueDate; 
invoiceObj.note = invoiceNote; 
invoiceObj.terms = invoiceTerms; 
invoiceObj.merchant_info = {}; 
invoiceObj.merchant_info.email = merchantEmail; 
invoiceObj.merchant_info.business_name = merchantBusinessName; 
invoiceObj.merchant_info.address = {}; 
invoiceObj.merchant_info.address.line1 = merchantAddress1; 
invoiceObj.merchant_info.address.line2 = merchantAddress2; 
invoiceObj.merchant_info.address.city = merchantAddressCity; 
invoiceObj.merchant_info.address.state = merchantAddressState; 
invoiceObj.merchant_info.address.postal_code = merchantAddressPC; 
invoiceObj.merchant_info.address.country_code = merchantAddressCC; 
invoiceObj.merchant_info.website = merchantWebsite; 
invoiceObj.billing_info = []; 
invoiceObj.billing_info[0] = {}; 
invoiceObj.billing_info[0].email = billingEmail; 
invoiceObj.billing_info[0].first_name = billingName1; 
invoiceObj.billing_info[0].last_name = billingName2; 
invoiceObj.billing_info[0].business_name = billingBusinessName; 
invoiceObj.billing_info[0].language = billingLanguage; 
invoiceObj.items = []; 
invoiceObj.items[0] = {}; 
invoiceObj.items[0].name = itemName; 
invoiceObj.items[0].description =itemDescription; 
invoiceObj.items[0].quantity = itemQuantity; 
invoiceObj.items[0].unit_price = {}; 
invoiceObj.items[0].unit_price.currency = itemUnitPriceCurrency; 
invoiceObj.items[0].unit_price.value = itemUnitPrice; 
invoiceObj.items[0].tax = {}; 
invoiceObj.items[0].tax.name = itemTaxName; 
invoiceObj.items[0].tax.percent = itemTaxPercent; 
var uriCreate = "https://api.paypal.com/v1/invoicing/invoices/"; 
var uriCreateSandbox = "https://api.sandbox.paypal.com/v1/invoicing/invoices/"; 
if( sandboxmode ){ uriCreate = uriCreateSandbox; } 
var responseCreate = httpClient.begin() 
                       .bearer( oauthToken ) 
                       .body( JSON.stringify( invoiceObj ), "application/json" ) 
                       .post( uriCreate ); 
accessLog += "---POST request--- " + responseCreate.getStatusCode() + "\n"; 
accessLog += responseCreate.getResponseAsString() + "\n"; 

//// == Data Updating == 
retVal.put( "q_accessLog", accessLog ); 

[Invoice Submission:"1. Register Invoice content" screen]

[PayPal Invoice]

[Data Items list]

[Free Download]
<Similar Models>
<<Related Articles>>

[Japanese Entry (ε’Œζ–‡θ¨˜δΊ‹)]