Skip to content

Commit

Permalink
Merge b710a71 into f2f016d
Browse files Browse the repository at this point in the history
  • Loading branch information
anniel-stripe committed Feb 16, 2023
2 parents f2f016d + b710a71 commit 0cd4d94
Show file tree
Hide file tree
Showing 23 changed files with 533 additions and 31 deletions.
2 changes: 1 addition & 1 deletion OPENAPI_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v224
v225
1 change: 1 addition & 0 deletions examples/webhook-signing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Verify the events that Stripe sends to your webhook endpoints. Additional detail
Available examples:
- [`express`](./express) - Express 4
- [`koa`](./koa) - Koa 2
- [`nextjs`](./nextjs) - NextJS 13

### Requirements

Expand Down
3 changes: 2 additions & 1 deletion examples/webhook-signing/express/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@types/koa": "^2.13.5",
"@types/koa-bodyparser": "^4.3.10",
"@types/node": "^13.1.4",
"typescript": "^4.8.3"
"typescript": "^4.8.3",
"ts-node": "^10.9.1"
}
}
1 change: 1 addition & 0 deletions examples/webhook-signing/nextjs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.next
17 changes: 17 additions & 0 deletions examples/webhook-signing/nextjs/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env -S npm run-script run

/*
ATTENTION: this file exists to support github/stripe/stripe-node example test infrastructure
You don't need to copy this file to your NextJS project.
*/

import child_process from 'child_process';

const process = child_process.exec('npm run dev --verbose');
process.stdout.on('data', (line) => {
const match = /url: (.*)/gm.exec(line);
if (match) {
console.log(`Webhook endpoint available at ${match[1]}/api/webhooks`);
}
console.log(line);
});
5 changes: 5 additions & 0 deletions examples/webhook-signing/nextjs/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
28 changes: 28 additions & 0 deletions examples/webhook-signing/nextjs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "webhook-signing-example-nextjs",
"version": "1.0.0",
"description": "NextJS webhook parsing sample",
"repository": {},
"main": "./main.ts",
"scripts": {
"run": "ts-node-transpile-only ./main.ts",
"prepare": "../prepare.sh",
"dev": "next dev --port 0"
},
"author": "",
"license": "ISC",
"dependencies": {
"dotenv": "^8.2.0",
"next": "^13.1.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"stripe": "^11.9.1"
},
"devDependencies": {
"@types/node": "^13.1.4",
"@types/react": "18.0.27",
"eslint": "^8.33.0",
"typescript": "^4.8.3",
"ts-node": "^10.9.1"
}
}
74 changes: 74 additions & 0 deletions examples/webhook-signing/nextjs/pages/api/webhooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import Stripe from 'stripe';
import {NextApiRequest, NextApiResponse} from 'next';

const handler = async (
req: NextApiRequest,
res: NextApiResponse
): Promise<void> => {
const stripe = new Stripe(process.env.STRIPE_WEBHOOK_SECRET, {
apiVersion: '2022-11-15',
});

const webhookSecret: string = process.env.STRIPE_WEBHOOK_SECRET;

if (req.method === 'POST') {
const sig = req.headers['stripe-signature'];

let event: Stripe.Event;

try {
const body = await buffer(req);
event = stripe.webhooks.constructEvent(body, sig, webhookSecret);
} catch (err) {
// On error, log and return the error message
console.log(`❌ Error message: ${err.message}`);
res.status(400).send(`Webhook Error: ${err.message}`);
return;
}

// Successfully constructed event
console.log('✅ Success:', event.id);

// Cast event data to Stripe object
if (event.type === 'payment_intent.succeeded') {
const stripeObject: Stripe.PaymentIntent = event.data
.object as Stripe.PaymentIntent;
console.log(`💰 PaymentIntent status: ${stripeObject.status}`);
} else if (event.type === 'charge.succeeded') {
const charge = event.data.object as Stripe.Charge;
console.log(`💵 Charge id: ${charge.id}`);
} else {
console.warn(`🤷‍♀️ Unhandled event type: ${event.type}`);
}

// Return a response to acknowledge receipt of the event
res.json({received: true});
} else {
res.setHeader('Allow', 'POST');
res.status(405).end('Method Not Allowed');
}
};

export const config = {
api: {
bodyParser: false,
},
};

const buffer = (req: NextApiRequest) => {
return new Promise<Buffer>((resolve, reject) => {
const chunks: Buffer[] = [];

req.on('data', (chunk: Buffer) => {
chunks.push(chunk);
});

req.on('end', () => {
resolve(Buffer.concat(chunks));
});

req.on('error', reject);
});
};

export default handler;
4 changes: 3 additions & 1 deletion test/Integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as childProcess from 'child_process';
const testUtils = require('../testUtils');

describe('Integration test', function() {
this.timeout(30000);
this.timeout(50000);
const testExec = (cmd: string): Promise<void> => {
const child = childProcess.exec(cmd);

Expand Down Expand Up @@ -89,4 +89,6 @@ describe('Integration test', function() {
it('Webhook sample express', () => runWebhookTest('express'));

it('Webhook sample koa', () => runWebhookTest('koa'));

it('Webhook sample nextjs', () => runWebhookTest('nextjs'));
});
16 changes: 15 additions & 1 deletion test/resources/generated_examples_test.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

'use strict';

const stripe = require('../../testUtils').getStripeMockClient();
const testUtils = require('../../testUtils');
const stripe = testUtils.getStripeMockClient();
const expect = require('chai').expect;

describe('Accounts', function() {
Expand Down Expand Up @@ -192,6 +193,19 @@ describe('Accounts', function() {
);
expect(person).not.to.be.null;
});

it('retrieve method', async function() {
const stripe = testUtils.createMockClient([
{
method: 'GET',
path: '/v1/accounts/acc_123',
response:
'{"business_profile":{"mcc":"mcc","name":"name","product_description":"product_description","support_address":{"city":"city","country":"country","line1":"line1","line2":"line2","postal_code":"postal_code","state":"state"},"support_email":"support_email","support_phone":"support_phone","support_url":"support_url","url":"url"},"business_type":"government_entity","capabilities":{"acss_debit_payments":"inactive","affirm_payments":"pending","afterpay_clearpay_payments":"inactive","au_becs_debit_payments":"active","bacs_debit_payments":"active","bancontact_payments":"inactive","bank_transfer_payments":"pending","blik_payments":"inactive","boleto_payments":"inactive","card_issuing":"active","card_payments":"active","cartes_bancaires_payments":"active","cashapp_payments":"active","eps_payments":"inactive","fpx_payments":"active","giropay_payments":"active","grabpay_payments":"pending","ideal_payments":"inactive","india_international_payments":"inactive","jcb_payments":"inactive","klarna_payments":"active","konbini_payments":"active","legacy_payments":"active","link_payments":"inactive","oxxo_payments":"pending","p24_payments":"inactive","paynow_payments":"active","promptpay_payments":"active","sepa_debit_payments":"inactive","sofort_payments":"active","tax_reporting_us_1099_k":"inactive","tax_reporting_us_1099_misc":"pending","transfers":"inactive","treasury":"pending","us_bank_account_ach_payments":"pending","zip_payments":"pending"},"charges_enabled":true,"company":{"address":{"city":"city","country":"country","line1":"line1","line2":"line2","postal_code":"postal_code","state":"state"},"address_kana":{"city":"city","country":"country","line1":"line1","line2":"line2","postal_code":"postal_code","state":"state","town":"town"},"address_kanji":{"city":"city","country":"country","line1":"line1","line2":"line2","postal_code":"postal_code","state":"state","town":"town"},"directors_provided":true,"executives_provided":true,"name":"name","name_kana":"name_kana","name_kanji":"name_kanji","owners_provided":true,"ownership_declaration":{"date":"3076014","ip":"ip","user_agent":"user_agent"},"phone":"phone","structure":"sole_establishment","tax_id_provided":true,"tax_id_registrar":"tax_id_registrar","vat_id_provided":true,"verification":{"document":{"back":{"created":"1028554472","expires_at":"833811170","filename":"filename","id":"obj_123","links":null,"object":"file","purpose":"finance_report_run","size":3530753,"title":"title","type":"type","url":"url"},"details":"details","details_code":"details_code","front":{"created":"1028554472","expires_at":"833811170","filename":"filename","id":"obj_123","links":null,"object":"file","purpose":"finance_report_run","size":3530753,"title":"title","type":"type","url":"url"}}}},"controller":{"application":{"loss_liable":true,"onboarding_owner":true,"pricing_controls":true},"dashboard":{"type":"express"},"is_controller":true,"type":"account"},"country":"country","created":"1028554472","default_currency":"default_currency","details_submitted":true,"email":"email","external_accounts":null,"future_requirements":{"alternatives":[{"alternative_fields_due":["alternative_fields_due"],"original_fields_due":["original_fields_due"]}],"current_deadline":"270965154","currently_due":["currently_due"],"disabled_reason":"disabled_reason","errors":[{"code":"verification_document_failed_copy","reason":"reason","requirement":"requirement"}],"eventually_due":["eventually_due"],"past_due":["past_due"],"pending_verification":["pending_verification"]},"id":"obj_123","individual":{"account":"account","address":{"city":"city","country":"country","line1":"line1","line2":"line2","postal_code":"postal_code","state":"state"},"address_kana":{"city":"city","country":"country","line1":"line1","line2":"line2","postal_code":"postal_code","state":"state","town":"town"},"address_kanji":{"city":"city","country":"country","line1":"line1","line2":"line2","postal_code":"postal_code","state":"state","town":"town"},"created":"1028554472","dob":{"day":99228,"month":104080000,"year":3704893},"email":"email","first_name":"first_name","first_name_kana":"first_name_kana","first_name_kanji":"first_name_kanji","full_name_aliases":["full_name_aliases"],"future_requirements":{"alternatives":[{"alternative_fields_due":["alternative_fields_due"],"original_fields_due":["original_fields_due"]}],"currently_due":["currently_due"],"errors":[{"code":"verification_document_failed_copy","reason":"reason","requirement":"requirement"}],"eventually_due":["eventually_due"],"past_due":["past_due"],"pending_verification":["pending_verification"]},"gender":"gender","id":"obj_123","id_number_provided":true,"id_number_secondary_provided":true,"last_name":"last_name","last_name_kana":"last_name_kana","last_name_kanji":"last_name_kanji","maiden_name":"maiden_name","metadata":{"undefined":"metadata"},"nationality":"nationality","object":"person","phone":"phone","political_exposure":"none","registered_address":{"city":"city","country":"country","line1":"line1","line2":"line2","postal_code":"postal_code","state":"state"},"relationship":{"director":true,"executive":true,"owner":true,"percent_ownership":760989685,"representative":true,"title":"title"},"requirements":{"alternatives":[{"alternative_fields_due":["alternative_fields_due"],"original_fields_due":["original_fields_due"]}],"currently_due":["currently_due"],"errors":[{"code":"verification_document_failed_copy","reason":"reason","requirement":"requirement"}],"eventually_due":["eventually_due"],"past_due":["past_due"],"pending_verification":["pending_verification"]},"ssn_last_4_provided":true,"verification":{"additional_document":{"back":{"created":"1028554472","expires_at":"833811170","filename":"filename","id":"obj_123","links":null,"object":"file","purpose":"finance_report_run","size":3530753,"title":"title","type":"type","url":"url"},"details":"details","details_code":"details_code","front":{"created":"1028554472","expires_at":"833811170","filename":"filename","id":"obj_123","links":null,"object":"file","purpose":"finance_report_run","size":3530753,"title":"title","type":"type","url":"url"}},"details":"details","details_code":"details_code","document":{"back":{"created":"1028554472","expires_at":"833811170","filename":"filename","id":"obj_123","links":null,"object":"file","purpose":"finance_report_run","size":3530753,"title":"title","type":"type","url":"url"},"details":"details","details_code":"details_code","front":{"created":"1028554472","expires_at":"833811170","filename":"filename","id":"obj_123","links":null,"object":"file","purpose":"finance_report_run","size":3530753,"title":"title","type":"type","url":"url"}},"status":"status"}},"metadata":{"undefined":"metadata"},"object":"account","payouts_enabled":true,"requirements":{"alternatives":[{"alternative_fields_due":["alternative_fields_due"],"original_fields_due":["original_fields_due"]}],"current_deadline":"270965154","currently_due":["currently_due"],"disabled_reason":"disabled_reason","errors":[{"code":"verification_document_failed_copy","reason":"reason","requirement":"requirement"}],"eventually_due":["eventually_due"],"past_due":["past_due"],"pending_verification":["pending_verification"]},"settings":{"bacs_debit_payments":{"display_name":"display_name"},"branding":{"icon":{"created":"1028554472","expires_at":"833811170","filename":"filename","id":"obj_123","links":null,"object":"file","purpose":"finance_report_run","size":3530753,"title":"title","type":"type","url":"url"},"logo":{"created":"1028554472","expires_at":"833811170","filename":"filename","id":"obj_123","links":null,"object":"file","purpose":"finance_report_run","size":3530753,"title":"title","type":"type","url":"url"},"primary_color":"primary_color","secondary_color":"secondary_color"},"card_issuing":{"tos_acceptance":{"date":3076014,"ip":"ip","user_agent":"user_agent"}},"card_payments":{"decline_on":{"avs_failure":true,"cvc_failure":true},"statement_descriptor_prefix":"statement_descriptor_prefix","statement_descriptor_prefix_kana":"statement_descriptor_prefix_kana","statement_descriptor_prefix_kanji":"statement_descriptor_prefix_kanji"},"dashboard":{"display_name":"display_name","timezone":"timezone"},"payments":{"statement_descriptor":"statement_descriptor","statement_descriptor_kana":"statement_descriptor_kana","statement_descriptor_kanji":"statement_descriptor_kanji","statement_descriptor_prefix_kana":"statement_descriptor_prefix_kana","statement_descriptor_prefix_kanji":"statement_descriptor_prefix_kanji"},"payouts":{"debit_negative_balances":true,"schedule":{"delay_days":1647351405,"interval":"interval","monthly_anchor":1920305369,"weekly_anchor":"weekly_anchor"},"statement_descriptor":"statement_descriptor"},"sepa_debit_payments":{"creditor_id":"creditor_id"},"treasury":{"tos_acceptance":{"date":3076014,"ip":"ip","user_agent":"user_agent"}}},"tos_acceptance":{"date":"3076014","ip":"ip","service_agreement":"service_agreement","user_agent":"user_agent"},"type":"custom"}',
},
]);
const account = await stripe.accounts.retrieve('acc_123');
expect(account).not.to.be.null;
});
});

describe('Apps.Secrets', function() {
Expand Down
95 changes: 95 additions & 0 deletions types/Checkout/Sessions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ declare module 'stripe' {
*/
currency_conversion?: Session.CurrencyConversion | null;

/**
* Collect additional information from your customer using custom fields. Up to 2 fields are supported.
*/
custom_fields: Array<Session.CustomField>;

custom_text: Session.CustomText;

/**
Expand Down Expand Up @@ -479,6 +484,96 @@ declare module 'stripe' {
}
}

interface CustomField {
/**
* Configuration for `type=dropdown` fields.
*/
dropdown: CustomField.Dropdown | null;

/**
* String of your choice that your integration can use to reconcile this field. Must be unique to this field, alphanumeric, and up to 200 characters.
*/
key: string;

label: CustomField.Label;

/**
* Configuration for `type=numeric` fields.
*/
numeric: CustomField.Numeric | null;

/**
* Whether the customer is required to complete the field before completing the Checkout Session. Defaults to `false`.
*/
optional: boolean;

/**
* Configuration for `type=text` fields.
*/
text: CustomField.Text | null;

/**
* The type of the field.
*/
type: CustomField.Type;
}

namespace CustomField {
interface Dropdown {
/**
* The options available for the customer to select. Up to 200 options allowed.
*/
options: Array<Dropdown.Option>;

/**
* The option selected by the customer. This will be the `value` for the option.
*/
value: string | null;
}

namespace Dropdown {
interface Option {
/**
* The label for the option, displayed to the customer. Up to 100 characters.
*/
label: string;

/**
* The value for this option, not displayed to the customer, used by your integration to reconcile the option selected by the customer. Must be unique to this option, alphanumeric, and up to 100 characters.
*/
value: string;
}
}

interface Label {
/**
* Custom text for the label, displayed to the customer. Up to 50 characters.
*/
custom: string | null;

/**
* The type of the label.
*/
type: 'custom';
}

interface Numeric {
/**
* The value entered by the customer, containing only digits.
*/
value: string | null;
}

interface Text {
/**
* The value entered by the customer.
*/
value: string | null;
}

type Type = 'dropdown' | 'numeric' | 'text';
}

interface CustomText {
/**
* Custom text that should be displayed alongside shipping address collection.
Expand Down
69 changes: 69 additions & 0 deletions types/Checkout/SessionsResource.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ declare module 'stripe' {
*/
currency?: string;

/**
* Collect additional information from your customer using custom fields. Up to 2 fields are supported.
*/
custom_fields?: Array<SessionCreateParams.CustomField>;

/**
* Display additional text for your customers using custom text.
*/
Expand Down Expand Up @@ -310,6 +315,70 @@ declare module 'stripe' {
type Shipping = 'auto' | 'never';
}

interface CustomField {
/**
* Configuration for `type=dropdown` fields.
*/
dropdown?: CustomField.Dropdown;

/**
* String of your choice that your integration can use to reconcile this field. Must be unique to this field, alphanumeric, and up to 200 characters.
*/
key: string;

/**
* The label for the field, displayed to the customer.
*/
label: CustomField.Label;

/**
* Whether the customer is required to complete the field before completing the Checkout Session. Defaults to `false`.
*/
optional?: boolean;

/**
* The type of the field.
*/
type: CustomField.Type;
}

namespace CustomField {
interface Dropdown {
/**
* The options available for the customer to select. Up to 200 options allowed.
*/
options: Array<Dropdown.Option>;
}

namespace Dropdown {
interface Option {
/**
* The label for the option, displayed to the customer. Up to 100 characters.
*/
label: string;

/**
* The value for this option, not displayed to the customer, used by your integration to reconcile the option selected by the customer. Must be unique to this option, alphanumeric, and up to 100 characters.
*/
value: string;
}
}

interface Label {
/**
* Custom text for the label, displayed to the customer. Up to 50 characters.
*/
custom: string;

/**
* The type of the label.
*/
type: 'custom';
}

type Type = 'dropdown' | 'numeric' | 'text';
}

interface CustomText {
/**
* Custom text that should be displayed alongside shipping address collection.
Expand Down
Loading

0 comments on commit 0cd4d94

Please sign in to comment.