Stripe: 請求書をメール送付 (Stripe: Send Invoice)
この工程は、Stripe 上の確定済み請求書を顧客にメール送付します。顧客はメールに含まれる支払いリンクから支払うことができます。
Configs:共通設定
  • 工程名
  • メモ
Configs
  • C1: API シークレットキーを設定した認証設定 *
  • C2: 確定済み請求書の ID *
  • C3: 顧客が使用したカードの情報を、次回以降の請求で再利用しない
  • C4: 支払い完了時、顧客に領収書をメール送付する

Notes

  • Stripe の API シークレットキーを取得するには https://dashboard.stripe.com/apikeys を参照してください(要 Stripe ログイン)
  • C3 がオフの場合、本アイテムでメール送付した請求書は、クレジットカードまたはデビットカードでのみ支払いできます

Capture

See also

Script (click to open)
  • 下記のスクリプトを記述した XML ファイルをダウンロードできます
    • stripe-invoice-send.xml (C) Questetra, Inc. (MIT License)
    • Professional をご利用であればファイルの内容を改変することでオリジナルのアドオンとして活用できます


const STRIPE_API_VERSION = '2022-08-01';

main();

function main(){
    //// == Config Retrieving / 工程コンフィグの参照 ==
    const auth = configs.get('conf_Auth');
    const invoiceId = retrieveInvoiceId();
    const notSetupFutureUsage = configs.getObject('conf_NotSetupFutureUsage');
    const sendReceipt = configs.getObject('conf_SendReceipt');

    //// == Calculating / 演算 ==
    const {customerId, paymentIntentId} = checkInvoice(auth, invoiceId);
    const customerEmail = checkCustomer(auth, customerId);
    if (!notSetupFutureUsage) { // カード情報を再利用するための設定を行う場合、支払方法をカードのみに設定する
        updatePaymentMethodTypes(auth, invoiceId);
    }
    updatePaymentIntent(auth, paymentIntentId, notSetupFutureUsage, sendReceipt, customerEmail);
    sendInvoice(auth, invoiceId);
}

/**
  * config から請求書の ID を読み出す。空文字列の場合はエラー
  * @return {String} invoiceId 請求書の ID
  */
function retrieveInvoiceId() {
    const invoiceId = engine.findData(configs.getObject('conf_InvoiceId'));
    if (invoiceId === null) {
        throw 'Invoice ID is blank.';
    }
    return invoiceId;
}

/**
  * 請求書のステータスをチェックし、顧客 ID と支払オブジェクトの ID を返す
  * - ステータスが open, uncollectible 以外の場合はエラー
  * - collection_method が send_invoice でない場合はエラー
  * @param {String} oauth 認証設定
  * @param {String} invoiceId 請求書の ID
  * @return {Object} returnObj
  * @return {String} returnObj.customerId 顧客 ID
  * @return {String} returnObj.paymentIntentId 支払オブジェクトの ID
  */
function checkInvoice(auth, invoiceId) {
    const apiUri = `https://api.stripe.com/v1/invoices/${encodeURIComponent(invoiceId)}`;
    const response = httpClient.begin()
        .authSetting(auth) // with "Authorization: Bearer XX"
        .header('Stripe-Version', STRIPE_API_VERSION)
        .get(apiUri);
    const status = response.getStatusCode();
    const responseStr = response.getResponseAsString();
    if (status !== 200) { // 削除済みの場合もエラーレスポンス
        engine.log(responseStr);
        throw `Failed to retrieve invoice. status: ${status}`;
    }
    const invoiceObj = JSON.parse(responseStr);
    switch (invoiceObj.status) {
        case 'draft':
            throw 'The invoice is still draft. It needs to be finalized first.';
        case 'paid':
            throw 'The invoice is already paid.';
        case 'void':
            throw 'The invoice is void.';
        default: // open or uncollectible
            // do nothing
    }
    if (invoiceObj.collection_method !== 'send_invoice') {
        // collection_method が send_invoice でない場合は、Stripe の仕様で請求書をメール送付できない
        throw 'The collection method of the invoice is not send_invoice.';
    }
    const customerId = invoiceObj.customer;
    const paymentIntentId = invoiceObj.payment_intent;
    return {customerId, paymentIntentId};
}

/**
  * 顧客をチェックし、顧客のメールアドレスを返す
  * - 顧客が削除済みの場合、エラー
  * - 顧客のメールアドレスが設定されていない場合、エラー
  * @param {String} oauth 認証設定
  * @param {String} customerId 顧客 ID
  * @return {String} customerEmail 顧客のメールアドレス
  */
function checkCustomer(auth, customerId) {
    const apiUri = `https://api.stripe.com/v1/customers/${customerId}`;
    const response = httpClient.begin()
        .authSetting(auth) // with "Authorization: Bearer XX"
        .header('Stripe-Version', STRIPE_API_VERSION)
        .get(apiUri);
    const status = response.getStatusCode();
    const responseStr = response.getResponseAsString();
    if (status !== 200) {
        engine.log(responseStr);
        throw `Failed to retrieve customer. status: ${status}`;
    }
    const customerObj = JSON.parse(responseStr);
    if (customerObj.deleted) {
        throw 'The customer is deleted.';
    }
    const customerEmail = customerObj.email;
    if (customerEmail === null) {
        // 顧客のメールアドレス以外に「請求先メールアドレス」もダッシュボードからは設定できるが、API では取得不可
        // 「請求先メールアドレス」の設定有無によらず、顧客のメールアドレスが設定されていない場合はエラーにする
        throw "The customer's email is not set. Update the customer or send the invoice from Stripe dashboard.";
    }
    return customerEmail;
}

/**
  * 請求書を更新し、支払方法をカードのみにする
  * @param {String} oauth 認証設定
  * @param {String} invoiceId 請求書の ID
  */
function updatePaymentMethodTypes(auth, invoiceId) {
    const apiUri = `https://api.stripe.com/v1/invoices/${encodeURIComponent(invoiceId)}`;
    const response = httpClient.begin()
        .authSetting(auth) // with "Authorization: Bearer XX"
        .header('Stripe-Version', STRIPE_API_VERSION)
        .formParam('payment_settings[payment_method_types][0]', 'card')
        .post(apiUri);
    const status = response.getStatusCode();
    const responseStr = response.getResponseAsString();
    if (status !== 200) { // 請求書のステータスが uncollectible の場合、更新に失敗
        engine.log(responseStr);
        throw `Failed to update invoice. status: ${status}`;
    }
}

/**
  * 支払オブジェクトを更新し、
  * - カード情報を再利用するための設定を行うかどうかを設定する
  * - 領収書送付先メールアドレスを設定する
  * @param {String} oauth 認証設定
  * @param {String} paymentIntentId 支払オブジェクトの ID
  * @param {boolean} notSetupFutureUsage カード情報を再利用するための設定を行わないかどうか
  * @param {boolean} sendReceipt 領収書をメール送付するかどうか
  * @param {String} customerEmail 顧客のメールアドレス
  */
function updatePaymentIntent(auth, paymentIntentId, notSetupFutureUsage, sendReceipt, customerEmail) {
    let setupFutureUsage = '';
    if (!notSetupFutureUsage) {
        setupFutureUsage = 'off_session';
    }
    let receiptEmail = '';
    if (sendReceipt) {
        receiptEmail = customerEmail;
    }
    const apiUri = `https://api.stripe.com/v1/payment_intents/${paymentIntentId}`;
    const response = httpClient.begin()
        .authSetting(auth) // with "Authorization: Bearer XX"
        .header('Stripe-Version', STRIPE_API_VERSION)
        .formParam('setup_future_usage', setupFutureUsage)
        .formParam('payment_method_options[card][setup_future_usage]', 'none') // カード固有の設定を解除
        .formParam('receipt_email', receiptEmail)
        .post(apiUri);
    const status = response.getStatusCode();
    const responseStr = response.getResponseAsString();
    if (status !== 200) {
        engine.log(responseStr);
        throw `Failed to update payment intent. status: ${status}`;
    }
}

/**
  * 請求書をメール送付する
  * @param {String} oauth 認証設定
  * @param {String} invoiceId 請求書の ID
  */
function sendInvoice(auth, invoiceId) {
    const apiUri = `https://api.stripe.com/v1/invoices/${encodeURIComponent(invoiceId)}/send`;
    const response = httpClient.begin()
        .authSetting(auth) // with "Authorization: Bearer XX"
        .header('Stripe-Version', STRIPE_API_VERSION)
        .post(apiUri);
    const status = response.getStatusCode();
    const responseStr = response.getResponseAsString();
    if (status !== 200) {
        // 顧客のメールアドレスが未設定の場合でもここではエラーにならず、レスポンスからも送信先はわからない
        engine.log(responseStr);
        throw `Failed to send invoice. status: ${status}`;
    }
}

    
%d人のブロガーが「いいね」をつけました。