2024-04-30 (C) Questetra, Inc. (MIT License) 3 2 https://support.questetra.com/bpmn-icons/service-task-stripe-metadata-update/ https://support.questetra.com/ja/bpmn-icons/service-task-stripe-metadata-update/ This item updates metadata of a customer, product or invoice on Stripe. Metadata Fields whose keys are not specified remain unchanged. この工程は、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= { const sourceStr = 'あいうえおかきくけこ'; const string = sourceStr.repeat(Math.floor(length / sourceStr.length)) + sourceStr.slice(0, length % sourceStr.length); return string; } /** * 設定の準備 - 顧客/商品/請求書 ID を文字型データ項目で指定 * @param id * @param keys * @param values */ const prepareConfigs = (id, keys, values) => { const auth = httpClient.createAuthSettingToken('stripe API Key', 'stripe-secret-key-12345'); configs.putObject('conf_Auth', auth); // 顧客/商品/請求書 ID が保存されている文字型データ項目(単一行)を準備し、設定 const idDef = engine.createDataDefinition('顧客/請求書 ID', 1, 'q_id', 'STRING_TEXTFIELD'); engine.setData(idDef, id); configs.putObject('conf_Id', idDef); for (let i = 0; i < FIELD_SIZE; i++) { configs.put(`conf_Key${i+1}`, keys[i]); configs.put(`conf_Value${i+1}`, values[i]); } } const SAMPLE_KEYS = new Array(FIELD_SIZE).fill('').map((_, i) => `フィールド ${i+1}`); const SAMPLE_VALUES = new Array(FIELD_SIZE).fill('').map((_, i) => `フィールド ${i+1} の値`); /** * 異常系のテスト * @param errorMsg */ const assertError = (errorMsg) => { let failed = false; try { main(); } catch (e) { failed = true; expect(e.message).toEqual(errorMsg); } if (!failed) { fail('No error was thrown.'); } }; /** * ID が空 */ test('ID is blank', () => { prepareConfigs('', SAMPLE_KEYS, SAMPLE_VALUES); assertError('Customer/Product/Invoice ID is blank.'); }); /** * ID が不正な形式 - アンダーバーを含まない */ test('ID is invalid - no underscore', () => { prepareConfigs('No-Underscore', SAMPLE_KEYS, SAMPLE_VALUES); assertError('Customer/Product/Invoice ID must start with "cus_", "prod_" or "in_".'); }); /** * ID が不正な形式 - アンダーバーより前の prefix が cus/prod/in でない */ test('ID is invalid - prefix not supported', () => { prepareConfigs('pi_00001', SAMPLE_KEYS, SAMPLE_VALUES); assertError('Customer/Product/Invoice ID must start with "cus_", "prod_" or "in_".'); }); /** * 値が指定されているのにキーが空 */ test('Key is blank while its value is set', () => { prepareConfigs('cus_00001', SAMPLE_KEYS, SAMPLE_VALUES); configs.put('conf_Key1', ''); assertError('C-V1 is set but C-K1 is blank.'); }); /** * キーが重複 */ test('Same key is set multiple times', () => { prepareConfigs('cus_00001', SAMPLE_KEYS, SAMPLE_VALUES); configs.put('conf_Key2', SAMPLE_KEYS[7]); // 2 番目と 8 番目が重複 assertError('Field Key "フィールド 8" is set multiple times.'); }); /** * キーが長すぎる */ test('Key is too long', () => { prepareConfigs('cus_00001', SAMPLE_KEYS, SAMPLE_VALUES); configs.put('conf_Key3', createString(KEY_MAX_LENGTH + 1)); assertError(`C-K3 exceeds the limit of ${KEY_MAX_LENGTH} characters.`); }); /** * 値が長すぎる */ test('Value is too long', () => { prepareConfigs('cus_00001', SAMPLE_KEYS, SAMPLE_VALUES); configs.put('conf_Value4', createString(VALUE_MAX_LENGTH + 1)); assertError(`C-V4 exceeds the limit of ${VALUE_MAX_LENGTH} characters.`); }); const ALL_BLANK = new Array(FIELD_SIZE).fill(''); /** * メタデータの key, value がすべて空の場合、エラー */ test('No fields to update', () => { prepareConfigs('cus_00001', ALL_BLANK, ALL_BLANK); assertError('No metadata fields to update.'); }); /** * メタデータを更新する API リクエストのテスト * @param {Object} request * @param request.url * @param request.method * @param request.headers * @param request.contentType * @param request.body * @param expectedUrl * @param keys * @param values */ const assertUpdateMetadataRequest = ({url, method, headers, contentType, body}, expectedUrl, keys, values) => { expect(url).toEqual(expectedUrl); expect(method).toEqual('POST'); expect(headers.get('Stripe-Version')).toEqual(STRIPE_VERSION); expect(contentType).startsWith('application/x-www-form-urlencoded'); const formParamList = []; for (let i = 0; i < FIELD_SIZE; i++) { pushToListIfNotBlank(formParamList, keys[i], values[i]); } const expectedBody = formParamList.join('&') .replace(/%20/g, '+'); // HttpRequestWrapper#formParam() はスペースを + に置き換える expect(body).toEqual(expectedBody); }; /** * key が空でない場合のみ、metadata[key]=value 形式の文字列を list に追加する * @param list * @param key * @param value */ const pushToListIfNotBlank = (list, key, value) => { if (key !== '') { const encodedParamName = encodeURIComponent(`metadata[${key}]`); list.push(`${encodedParamName}=${encodeURIComponent(value)}`); } }; /** * 成功 - 顧客 ID を文字型データ項目で指定した場合 * キー、値は最大文字数でテスト */ test('Success - Customer ID set by string-type data item', () => { const keys = JSON.parse(JSON.stringify(SAMPLE_KEYS)); // ディープコピー keys[6] = createString(KEY_MAX_LENGTH); const values = JSON.parse(JSON.stringify(SAMPLE_VALUES)); // ディープコピー values[7] = createString(VALUE_MAX_LENGTH); prepareConfigs('cus_00001', keys, values); const expectedUrl = 'https://api.stripe.com/v1/customers/cus_00001'; httpClient.setRequestHandler((request) => { assertUpdateMetadataRequest(request, expectedUrl, keys, values); return httpClient.createHttpResponse(200, 'application/json', '{}'); }); main(); }); /** * 成功 - 商品 ID を文字型データ項目で指定した場合 */ test('Success - Product ID set by string-type data item', () => { prepareConfigs('prod_00001', SAMPLE_KEYS, SAMPLE_VALUES); const expectedUrl = 'https://api.stripe.com/v1/products/prod_00001'; httpClient.setRequestHandler((request) => { assertUpdateMetadataRequest(request, expectedUrl, SAMPLE_KEYS, SAMPLE_VALUES); return httpClient.createHttpResponse(200, 'application/json', '{}'); }); main(); }); /** * 成功 - 請求書 ID を文字型データ項目で指定した場合 */ test('Success - Invoice ID set by string-type data item', () => { prepareConfigs('in_00001', SAMPLE_KEYS, SAMPLE_VALUES); const expectedUrl = 'https://api.stripe.com/v1/invoices/in_00001'; httpClient.setRequestHandler((request) => { assertUpdateMetadataRequest(request, expectedUrl, SAMPLE_KEYS, SAMPLE_VALUES); return httpClient.createHttpResponse(200, 'application/json', '{}'); }); main(); }); /** * メタデータを更新する HTTP リクエストで失敗 */ test('Fail to update metadata', () => { prepareConfigs('cus_00002', SAMPLE_KEYS, SAMPLE_VALUES); const expectedUrl = 'https://api.stripe.com/v1/customers/cus_00002'; httpClient.setRequestHandler((request) => { assertUpdateMetadataRequest(request, expectedUrl, SAMPLE_KEYS, SAMPLE_VALUES); return httpClient.createHttpResponse(400, 'application/json', '{}'); }); assertError('Failed to update metadata. status: 400'); }); /** * 成功 - key, value が空のものがある場合 */ test('Success - Some keys and values are blank', () => { const keys = JSON.parse(JSON.stringify(SAMPLE_KEYS)); // ディープコピー keys[1] = ''; keys[3] = ''; const values = JSON.parse(JSON.stringify(SAMPLE_VALUES)); // ディープコピー values[1] = ''; // key が空のものは値も空にする values[3] = ''; values[5] = ''; // value だけ空のものがあってもよい prepareConfigs('in_00002', keys, values); const expectedUrl = 'https://api.stripe.com/v1/invoices/in_00002'; httpClient.setRequestHandler((request) => { assertUpdateMetadataRequest(request, expectedUrl, keys, values); return httpClient.createHttpResponse(200, 'application/json', '{}'); }); main(); }); /** * 成功 - 顧客 ID を選択型データ項目で指定した場合 */ test('Success - Customer ID set by select-type data item', () => { prepareConfigs('', SAMPLE_KEYS, SAMPLE_VALUES); // 選択型データ項目を準備し、設定 const customerId = 'cus_00010'; const idDef = engine.createDataDefinition('顧客 ID を選択', 2, 'q_customerIdSelect', 'SELECT_SINGLE'); const select = new java.util.ArrayList(); const item = engine.createItem(customerId, `${customerId} を選択`); select.add(item); engine.setData(idDef, select); configs.putObject('conf_Id', idDef); const expectedUrl = 'https://api.stripe.com/v1/customers/cus_00010'; httpClient.setRequestHandler((request) => { assertUpdateMetadataRequest(request, expectedUrl, SAMPLE_KEYS, SAMPLE_VALUES); return httpClient.createHttpResponse(200, 'application/json', '{}'); }); main(); }); /** * ID を選択型データ項目で指定し、選択されていない場合 */ test('Customer/Invoice ID set by select-type data item and not selected', () => { prepareConfigs('cus_00011', SAMPLE_KEYS, SAMPLE_VALUES); // 選択型データ項目を準備し、未選択のまま設定 const idDef = engine.createDataDefinition('ID を選択', 2, 'q_idSelect', 'SELECT_SINGLE'); configs.putObject('conf_Id', idDef); assertError('Customer/Product/Invoice ID is not selected.'); }); /** * 成功 - 請求書 ID を固定値で指定した場合 */ test('Success - Invoice ID set as fixed value', () => { prepareConfigs('', SAMPLE_KEYS, SAMPLE_VALUES); configs.put('conf_Id', 'in_00011'); const expectedUrl = 'https://api.stripe.com/v1/invoices/in_00011'; httpClient.setRequestHandler((request) => { assertUpdateMetadataRequest(request, expectedUrl, SAMPLE_KEYS, SAMPLE_VALUES); return httpClient.createHttpResponse(200, 'application/json', '{}'); }); main(); }); ]]>