2022-09-08
(C) Questetra, Inc. (MIT License)
2
https://support.questetra.com/bpmn-icons/service-task-stripe-invoice-send/
https://support.questetra.com/ja/bpmn-icons/service-task-stripe-invoice-send/
This item sends a finalized invoice on Stripe to the customer by email. The customer can pay
the invoice via the payment link included in the email.
この工程は、Stripe 上の確定済み請求書を顧客にメール送付します。顧客はメールに含まれる支払いリンクから支払うことができます。
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAvJJREFUWEfF
l19I01EUxz/XKZqWNbVS6cH+UfSUGQlCD0EQRCkoFDkDUzENi6IgkB4sKEgjDStFnVlzalC9+BBR
IEFBEeVbFP3R/lhRpLRpKHO78dtv021u+pszfnv87Zzv+dxzzj33XoHOP6E1fvleGe9KJF/CDiSZ
CDIAo8d/BMkggn4BfVE27rX0ir9atOcEKC6WqdEOTkuoAOK0iALjAponY7jY0SF+zOYzK0CJSVYI
uAQkaAwcaDYm4VS7VTSH8g8JUFokryOpnGdgfzdBk7lTHAmmFRSgxCS7BBxYkOAeEQnd7VZRGKg5
A2BBVz4z2oxM+AF4at60kCsP1JJQ6dsTUwBKtxscvI+g4bRyjzljWOfdHVMAZSZZL+G4VpVI7AQ0
tFnFCUXDDaAMGWciv8PY55HEV3zHDTaSlWHlBigzySIJlvmoxsbBxHj4ngIOtllFpxug1CTNQMls
MjnbIS0d7t6etmpshe9DcKEmfACg3WwVpSpAoXyFIDNQZuMm2J2rfk1Ng6XL4NlTSE4Blwt6LOB0
QtEhkBKGvsCdHli9FnLzoe8h9L8MASfpN3eJLd4MDPscLFMeTTcgOhocDjXNi5fAr5+wYqUa+NOA
ClF9FiYnVdtHD2DnLpiYgNhYqKmGr5+DQoyYrSLJCyCDmdQ1gtEIdpv6r7LKbgscroKr9ZC/Dzpa
VIBr9VBeBYYoiDKotkLA/V7/svnGMVuFYuLugaAAx05C+ipIWa4KShe8eA7ZOdB4GQr2TwMMfoSM
NfDuLazfALY/YLdD9y148zp4GXwBgpag9goYk2BkGJ48hj15MDoK8QnQUAtZ26DrJrRaVMBRO9Sd
h7wC2JylAp87A9+G5ipBiCbU0ttbs6HiqFqCkA0XTCigCefchqFgFsWrO2TggxZcP5vpbRjJIAo7
rMfBbxDpPoo941i/w0gB0P04ViB0vZB4m0nXK5kXQtdL6X/JRLjXcp9M6Pcw8ULo+jTznXK6PU7n
O2q1+v0D2nRCMMki7aoAAAAASUVORK5CYII=
{
configs.put('conf_Auth', 'Stripe');
// 請求書 ID が保存されている文字型データ項目(単一行)を準備
const invoiceIdDef = engine.createDataDefinition('確定済み請求書の ID', 1, 'q_invoiceId', 'STRING_TEXTFIELD');
engine.setData(invoiceIdDef, invoiceId);
configs.putObject('conf_InvoiceId', invoiceIdDef);
configs.putObject('conf_NotSetupFutureUsage', notSetupFutureUsage);
configs.putObject('conf_SendReceipt', sendReceipt);
};
/**
* 請求書 ID が空
*/
test('Invoice ID is blank', () => {
prepareConfigs(null, false, true);
expect(execute).toThrow('Invoice ID is blank.');
});
/**
* 請求書を取得する API リクエストのテスト
* @param {Object} request
* @param request.url
* @param request.method
* @param request.headers
* @param invoiceId
*/
const assertGetInvoiceRequest = ({url, method, headers}, invoiceId) => {
expect(url).toEqual(`https://api.stripe.com/v1/invoices/${encodeURIComponent(invoiceId)}`);
expect(method).toEqual('GET');
expect(headers.get('Stripe-Version')).toEqual(STRIPE_VERSION);
};
/**
* 請求書を取得する HTTP リクエストで失敗
*/
test('Fail to retrieve invoice', () => {
prepareConfigs('in_00001', false, true);
httpClient.setRequestHandler((request) => {
assertGetInvoiceRequest(request, 'in_00001');
return httpClient.createHttpResponse(400, 'application/json', '{}');
});
expect(execute).toThrow('Failed to retrieve invoice. status: 400');
});
/**
* 請求書のステータスが不正 - ドラフト状態
*/
test('Invalid status - draft', () => {
prepareConfigs('in_00002', false, true);
httpClient.setRequestHandler((request) => {
assertGetInvoiceRequest(request, 'in_00002');
return httpClient.createHttpResponse(200, 'application/json', '{"status": "draft"}');
});
expect(execute).toThrow('The invoice is still draft. It needs to be finalized first.');
});
/**
* 請求書のステータスが不正 - 支払い済み
*/
test('Invalid status - paid', () => {
prepareConfigs('in_00003', false, true);
httpClient.setRequestHandler((request) => {
assertGetInvoiceRequest(request, 'in_00003');
return httpClient.createHttpResponse(200, 'application/json', '{"status": "paid"}');
});
expect(execute).toThrow('The invoice is already paid.');
});
/**
* 請求書のステータスが不正 - 無効
*/
test('Invalid status - void', () => {
prepareConfigs('in_00004', false, true);
httpClient.setRequestHandler((request) => {
assertGetInvoiceRequest(request, 'in_00004');
return httpClient.createHttpResponse(200, 'application/json', '{"status": "void"}');
});
expect(execute).toThrow('The invoice is void.');
});
/**
* 請求書の支払方法が send_invoice でない
*/
test('Invalid collection method', () => {
prepareConfigs('in_00005', false, true);
const invoiceObj = {
'status': 'open',
'collection_method': 'charge_automatically'
};
httpClient.setRequestHandler((request) => {
assertGetInvoiceRequest(request, 'in_00005');
return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(invoiceObj));
});
expect(execute).toThrow('The collection method of the invoice is not send_invoice.');
});
const SAMPLE_INVOICE_1 = {
'status': 'open',
'collection_method': 'send_invoice',
'customer': 'cus_00001',
'payment_intent': 'pi_00001'
};
const SAMPLE_INVOICE_2 = {
'status': 'open',
'collection_method': 'send_invoice',
'customer': 'cus_00002',
'payment_intent': 'pi_00002'
};
/**
* 顧客を取得する API リクエストのテスト
* @param {Object} request
* @param request.url
* @param request.method
* @param request.headers
* @param customerId
*/
const assertGetCustomerRequest = ({url, method, headers}, customerId) => {
expect(url).toEqual(`https://api.stripe.com/v1/customers/${customerId}`);
expect(method).toEqual('GET');
expect(headers.get('Stripe-Version')).toEqual(STRIPE_VERSION);
};
/**
* 顧客を取得する HTTP リクエストで失敗
*/
test('Fail to retrieve customer', () => {
prepareConfigs('in_00021', false, true);
let reqCount = 0;
httpClient.setRequestHandler((request) => {
if (reqCount === 0) {
assertGetInvoiceRequest(request, 'in_00021');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_INVOICE_1));
}
assertGetCustomerRequest(request, 'cus_00001');
return httpClient.createHttpResponse(400, 'application/json', '{}');
});
expect(execute).toThrow('Failed to retrieve customer. status: 400');
});
/**
* 顧客が削除済み
*/
test('Customer is deleted', () => {
prepareConfigs('in_00022', false, true);
let reqCount = 0;
httpClient.setRequestHandler((request) => {
if (reqCount === 0) {
assertGetInvoiceRequest(request, 'in_00022');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_INVOICE_2));
}
assertGetCustomerRequest(request, 'cus_00002');
return httpClient.createHttpResponse(200, 'application/json', '{"deleted": true}');
});
expect(execute).toThrow('The customer is deleted.');
});
/**
* 顧客のメールアドレスが未設定
*/
test('Customer email is null', () => {
prepareConfigs('in_00023', false, false);
let reqCount = 0;
httpClient.setRequestHandler((request) => {
if (reqCount === 0) {
assertGetInvoiceRequest(request, 'in_00023');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_INVOICE_1));
}
assertGetCustomerRequest(request, 'cus_00001');
return httpClient.createHttpResponse(200, 'application/json', '{"email": null}');
});
expect(execute)
.toThrow("The customer's email is not set. Update the customer or send the invoice from Stripe dashboard.");
});
const SAMPLE_CUSTOMER_1 = {
'email': 'cus1@example.com'
};
const SAMPLE_CUSTOMER_2 = {
'email': 'cus2@example.com'
};
/**
* 請求書を更新する API リクエストのテスト
* @param {Object} request
* @param request.url
* @param request.method
* @param request.headers
* @param request.contentType
* @param request.body
* @param invoiceId
*/
const assertUpdateInvoiceRequest = ({url, method, headers, contentType, body}, invoiceId) => {
expect(url).toEqual(`https://api.stripe.com/v1/invoices/${encodeURIComponent(invoiceId)}`);
expect(method).toEqual('POST');
expect(headers.get('Stripe-Version')).toEqual(STRIPE_VERSION);
expect(contentType).startsWith('application/x-www-form-urlencoded');
expect(body).toEqual(`${encodeURIComponent('payment_settings[payment_method_types][0]')}=card`);
};
/**
* 請求書を更新する HTTP リクエストで失敗
*/
test('Fail to update invoice', () => {
prepareConfigs('in_00051', false, true);
let reqCount = 0;
httpClient.setRequestHandler((request) => {
if (reqCount === 0) {
assertGetInvoiceRequest(request, 'in_00051');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_INVOICE_1));
}
if (reqCount === 1) {
assertGetCustomerRequest(request, 'cus_00001');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_CUSTOMER_1));
}
assertUpdateInvoiceRequest(request, 'in_00051');
return httpClient.createHttpResponse(400, 'application/json', '{}');
});
expect(execute).toThrow('Failed to update invoice. status: 400');
});
/**
* 支払オブジェクトを更新する API リクエストのテスト
* @param {Object} request
* @param request.url
* @param request.method
* @param request.headers
* @param request.contentType
* @param request.body
* @param paymentIntentId
* @param setupFutureUsage
* @param receiptEmail
*/
const assertUpdatePaymentIntentRequest = ({url, method, headers, contentType, body},
paymentIntentId, setupFutureUsage, receiptEmail) => {
expect(url).toEqual(`https://api.stripe.com/v1/payment_intents/${paymentIntentId}`);
expect(method).toEqual('POST');
expect(headers.get('Stripe-Version')).toEqual(STRIPE_VERSION);
expect(contentType).startsWith('application/x-www-form-urlencoded');
const expectedBody = `setup_future_usage=${setupFutureUsage ? 'off_session' : ''}`
+ `&${encodeURIComponent('payment_method_options[card][setup_future_usage]')}=none`
+ `&receipt_email=${encodeURIComponent(receiptEmail)}`;
expect(body).toEqual(expectedBody);
};
/**
* 支払オブジェクトを更新する HTTP リクエストで失敗
*/
test('Fail to update payment intent', () => {
prepareConfigs('in_00031', false, true);
let reqCount = 0;
httpClient.setRequestHandler((request) => {
if (reqCount === 0) {
assertGetInvoiceRequest(request, 'in_00031');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_INVOICE_1));
}
if (reqCount === 1) {
assertGetCustomerRequest(request, 'cus_00001');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_CUSTOMER_1));
}
if (reqCount === 2) {
assertUpdateInvoiceRequest(request, 'in_00031');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', '{}');
}
assertUpdatePaymentIntentRequest(request, 'pi_00001', true, 'cus1@example.com');
return httpClient.createHttpResponse(400, 'application/json', '{}');
});
expect(execute).toThrow('Failed to update payment intent. status: 400');
});
/**
* 請求書をメール送付する API リクエストのテスト
* @param {Object} request
* @param request.url
* @param request.method
* @param request.headers
* @param invoiceId
*/
const assertSendInvoiceRequest = ({url, method, headers}, invoiceId) => {
expect(url).toEqual(`https://api.stripe.com/v1/invoices/${encodeURIComponent(invoiceId)}/send`);
expect(method).toEqual('POST');
expect(headers.get('Stripe-Version')).toEqual(STRIPE_VERSION);
};
/**
* 請求書をメール送付する HTTP リクエストで失敗
*/
test('Fail to send invoice', () => {
prepareConfigs('in_00041', false, true);
let reqCount = 0;
httpClient.setRequestHandler((request) => {
if (reqCount === 0) {
assertGetInvoiceRequest(request, 'in_00041');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_INVOICE_1));
}
if (reqCount === 1) {
assertGetCustomerRequest(request, 'cus_00001');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_CUSTOMER_1));
}
if (reqCount === 2) {
assertUpdateInvoiceRequest(request, 'in_00041');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', '{}');
}
if (reqCount === 3) {
assertUpdatePaymentIntentRequest(request, 'pi_00001', true, 'cus1@example.com');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', '{}');
}
assertSendInvoiceRequest(request, 'in_00041');
return httpClient.createHttpResponse(400, 'application/json', '{}');
});
expect(execute).toThrow('Failed to send invoice. status: 400');
});
/**
* 成功 - 領収書を送付する
*/
test('Success - sendReceipt is true', () => {
prepareConfigs('in_00042', false, true);
let reqCount = 0;
httpClient.setRequestHandler((request) => {
if (reqCount === 0) {
assertGetInvoiceRequest(request, 'in_00042');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_INVOICE_2));
}
if (reqCount === 1) {
assertGetCustomerRequest(request, 'cus_00002');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_CUSTOMER_2));
}
if (reqCount === 2) {
assertUpdateInvoiceRequest(request, 'in_00042');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', '{}');
}
if (reqCount === 3) {
assertUpdatePaymentIntentRequest(request, 'pi_00002', true, 'cus2@example.com');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', '{}');
}
assertSendInvoiceRequest(request, 'in_00042');
return httpClient.createHttpResponse(200, 'application/json', '{}');
});
execute();
});
/**
* 成功 - 領収書を送付しない
*/
test('Success - sendReceipt is false', () => {
prepareConfigs('in_00044', false, false);
let reqCount = 0;
httpClient.setRequestHandler((request) => {
if (reqCount === 0) {
assertGetInvoiceRequest(request, 'in_00044');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_INVOICE_1));
}
if (reqCount === 1) {
assertGetCustomerRequest(request, 'cus_00001');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_CUSTOMER_1));
}
if (reqCount === 2) {
assertUpdateInvoiceRequest(request, 'in_00044');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', '{}');
}
if (reqCount === 3) {
assertUpdatePaymentIntentRequest(request, 'pi_00001', true, ''); // 領収書送付先メールアドレスが空に設定される
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', '{}');
}
assertSendInvoiceRequest(request, 'in_00044');
return httpClient.createHttpResponse(200, 'application/json', '{}');
});
execute();
});
/**
* 成功 - カード情報を再利用しない
*/
test('Success - Not reuse the card details', () => {
prepareConfigs('in_00051', true, true);
let reqCount = 0;
httpClient.setRequestHandler((request) => {
if (reqCount === 0) {
assertGetInvoiceRequest(request, 'in_00051');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_INVOICE_1));
}
if (reqCount === 1) {
assertGetCustomerRequest(request, 'cus_00001');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_CUSTOMER_1));
}
// 請求書の支払方法は更新されない
if (reqCount === 2) {
// setup_future_usage の設定が解除される
assertUpdatePaymentIntentRequest(request, 'pi_00001', false, 'cus1@example.com');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', '{}');
}
assertSendInvoiceRequest(request, 'in_00051');
return httpClient.createHttpResponse(200, 'application/json', '{}');
});
execute();
});
]]>