
Stripe: Create Draft Invoice
This item creates a draft invoice on Stripe. Draft invoices must be finalized separately before you can invoice or charge the customer.
Configs: Common
- Step Name
- Note
Configs
- C1: Authorization Setting in which API Secret Key is set *
- C2-deprecated: Customer ID#{EL}
- C2: Customer ID *
- C3: Description (Displayed in the invoice as ‘memo’)#{EL}
- C4: Currency (If not selected, defaults to that of the customer)
- C5: List of item names, unit amounts, and quantities
- C6: Data item to save ID of the invoice
- C7: Data item to save URL of the invoice detail page
Notes
- If C2-deprecated is set, you must set C2: Customer ID
- To get Stripe’s API Secret Key, visit https://dashboard.stripe.com/apikeys (Stripe login required)
- The currency of the invoice is determined by the customer’s setting or the Stripe account setting
- For more details, see the Stripe Support Page
- Unit amounts must be provided in the currency’s smallest unit
- For example, to charge 10 USD, provide 1000 (that is, 1000 cents)
- For more details, see the Stripe Documentation
Capture

See also
Script (click to open)
- An XML file that contains the code below is available to download
- stripe-invoice-create.xml (C) Questetra, Inc. (MIT License)
- If you are using Professional, you can modify the contents of this file and use it as your own add-on
const STRIPE_API_VERSION = '2022-08-01';
main();
function main(){
//// == Config Retrieving / 工程コンフィグの参照 ==
const auth = configs.get('conf_Auth');
const customerId = retrieveCustomerId();
const description = configs.get('conf_Description');
const currency = configs.get('conf_Currency');
const items = retrieveItems();
const invoiceIdDef = configs.getObject('conf_InvoiceId');
const invoiceUrlDef = configs.getObject('conf_InvoiceUrl');
//// == Calculating / 演算 ==
const invoiceId = createInvoice(auth, customerId, description, currency);
items.forEach(item => {
attachInvoiceItem(auth, customerId, invoiceId, item);
});
//// == Data Updating / ワークフローデータへの代入 ==
if (invoiceIdDef !== null) { // STRING
engine.setData(invoiceIdDef, invoiceId);
}
if (invoiceUrlDef !== null) { // STRING
engine.setData(invoiceUrlDef, `https://dashboard.stripe.com/invoices/${invoiceId}`);
}
}
/**
* config から顧客 ID を読み出す。空文字列の場合はエラー
* @return {String} customerId 顧客 ID
*/
function retrieveCustomerId() {
const customerIdV1 = configs.get('conf_CustomerId');
if (customerIdV1 !== null && customerIdV1 !== '') {
return customerIdV1;
}
let customerId = configs.get('conf_CustomerId_V2'); // 固定値の場合
const customerIdDef = configs.getObject('conf_CustomerId_V2');
if (customerIdDef !== null) {
if (customerIdDef.matchDataType('SELECT_SINGLE')) { // 選択型データ項目の場合
const select = engine.findData(customerIdDef);
if (select === null || select.size() === 0) { // 未選択
throw 'Customer ID is not selected.';
}
customerId = select.get(0).getValue();
} else { // 文字型データ項目の場合
customerId = engine.findData(customerIdDef);
}
}
if (customerId === null || customerId === '') {
throw 'Customer ID is blank.';
}
return customerId;
}
/**
* config からラインアイテムの情報を読み出す
* 入力値が不正な場合や、請求額合計が 8 桁(Stripe の上限値)を超える場合はエラー
* @return {List<Object>} items
* @return {String} items[].description アイテム名
* @return {String} items[].unitAmount 単価
* @return {String} items[].quantity 数量
*/
function retrieveItems() {
const itemsDef = configs.getObject('conf_LineItems');
if (itemsDef === null) {
return [];
}
const table = engine.findData(itemsDef); // ScriptListArray
// テーブルのサイズをチェック
if (table === null) {
return [];
}
if (table.size() > httpClient.getRequestingLimit() - 1) { // 請求書ドラフトの作成で 1 回、各アイテムの添付でリクエスト 1 回
throw 'Too many line items. Number of necessary HTTP requests exceeds the limit.';
}
// サブデータ項目の数と型をチェック
const subDataDefs = itemsDef.getSubDataDefinitions(); // List<SubDataDefinitionView>
if (subDataDefs.size() < 3) {
throw 'The line items must include item description, unit amount, and quantity.';
}
if (!subDataDefs.get(0).matchDataType('STRING')) {
throw 'Line item name (1st column) must be STRING.';
}
if (!subDataDefs.get(1).matchDataType('DECIMAL')) {
throw 'Line item unit amount (2nd column) must be DECIMAL.';
}
if (!subDataDefs.get(2).matchDataType('DECIMAL')) {
throw 'Line item quantity (3rd column) must be DECIMAL.';
}
// オブジェクトの配列に整形
const items = [];
let totalAmount = 0;
for (let i = 0; i < table.size(); i++) {
const row = table.getRow(i);
const description = row.getObject(0);
if (description === null) {
throw `Line item ${i+1} is invalid. Item name must not be blank.`;
}
const unitAmount = parseBigDecimalToLong(row.getObject(1), 'Unit amount', i);
const quantity = parseBigDecimalToLong(row.getObject(2), 'Quantity', i);
totalAmount += unitAmount * quantity;
// 請求額は Stripe の制限で最大 8 桁まで。IDR のみ 12 桁までだが、ここでは一律で 8 桁までとする
if (totalAmount > 99999999) {
throw 'The total amount of line items must be less than 100000000.';
}
const item = {description, unitAmount, quantity};
items.push(item);
}
return items;
}
/**
* BigDecimal の数値を long にパースして返す
* 小数点以下が0でない場合、負の数の場合はエラー
* @param {BigDecimal} bigDecimal 数値
* @param {String} label エラー出力用ラベル
* @param {Number} i エラー出力用インデックス
* @return {long} longValue 変換後の数値
*/
function parseBigDecimalToLong(bigDecimal, label, i) {
let longValue;
try {
longValue = bigDecimal.longValueExact();
} catch (e) {
throw `Line item ${i+1} is invalid. ${label} must be integer.`;
}
if (longValue < 0) {
throw `Line item ${i+1} is invalid. ${label} must not be negative.`;
}
return longValue;
}
/**
* 請求書のドラフトを作成する
* @param {String} oauth 認証設定
* @param {String} customerId 顧客 ID
* @param {String} description 説明
* @param {String} currency 通貨
* @return {String} invoiceId 作成された請求書ドラフトの ID
*/
function createInvoice(auth, customerId, description, currency) {
const apiUri = 'https://api.stripe.com/v1/invoices';
const request = httpClient.begin()
.authSetting(auth) // with "Authorization: Bearer XX"
.header('Stripe-Version', STRIPE_API_VERSION)
.formParam('customer', customerId)
.formParam('description', description)
.formParam('auto_advance', 'false'); // ドラフトが自動で確定されないよう false に
if (currency !== '') {
request.formParam('currency', currency);
}
const response = request.post(apiUri);
const status = response.getStatusCode();
const responseStr = response.getResponseAsString();
if (status !== 200) {
engine.log(responseStr);
throw `Failed to create draft invoice. status: ${status}`;
}
return JSON.parse(responseStr).id;
}
/**
* 請求書ドラフトにラインアイテムを追加する
* @param {String} oauth 認証設定
* @param {String} customerId 顧客 ID
* @param {String} invoiceId 請求書ドラフトの ID
* @param {Object} item
* @param {String} item.description アイテム名
* @param {long} item.unitAmount 単価
* @param {long} item.quantity 数量
*/
function attachInvoiceItem(auth, customerId, invoiceId, {description, unitAmount, quantity}) {
const apiUri = 'https://api.stripe.com/v1/invoiceitems';
const response = httpClient.begin()
.authSetting(auth) // with "Authorization: Bearer XX"
.header('Stripe-Version', STRIPE_API_VERSION)
.formParam('customer', customerId)
.formParam('invoice', invoiceId)
.formParam('description', description)
.formParam('unit_amount', unitAmount.toString())
.formParam('quantity', quantity.toString())
.post(apiUri);
const status = response.getStatusCode();
const responseStr = response.getResponseAsString();
if (status !== 200) {
engine.log(responseStr);
throw `Failed to attach an invoice item. status: ${status}`;
}
}