Skip to content

Commit fdb868c

Browse files
jsteele-stripedackers86cjavilla-stripeabierbaumwillbattel
authored
chore: 2022-08-23 release (#423)
* feat(firestore-stripe-payments): upgraded to the new emulator suite (#400) tests(firestore-stripe-payments): upgraded to the new emulator suite * fix(firestore-stripe-payments): added env stripe key check on test setup * fix(firestore-stripe-payments): added env stripe key check on test setup * formatted code * Remove link to old repo * feat(firestore-stripe-payments): Allow configuring min instances for createCheckoutSession (#375) * feat: Allow configuring min instances for createCheckoutSession * Update firestore-stripe-payments/extension.yaml * Update firestore-stripe-payments/extension.yaml Co-authored-by: Jonathan Steele <[email protected]> * chore(firestore-stripe-payments): updated scripts for CI and local testing (#415) * chore(firestore-stripe-payments): updated scripts for ci and local testing * chore(firestore-stripe-payments): added testing typescript configuration, fixes jest warnings in files * fix(firestore-stripe-web-sdk): locked sinon types to 10.0.6 * chore(firestore-stripe-payments): removed obsolete comments * fix(firestore-stripe-payments): `minInstances` parameter expects number * docs(firestore-stripe-payments): Update APIs for restricted key (#411) Prices API replaces the Plans API, per https://stripe.com/docs/api/plans * docs(firestore-stripe-payments): Add extra context to `CREATE_CHECKOUT_SESSION_MIN_INSTANCES` config parameter * fix(firestore-stripe-payments): added subscription payments prices, test and refactor included (#393) * fix(firestore-stripe-payments): added subscription payments prices, tests and refactor included * chore: locked firebase tools workflow to 10.9.2 * added setup emulator for collection helper functions * chore: commiting empty env files * chore: updated ci firebase tools to latest * pinned sinon types at 10.0.6 * feat(firestore-stripe-payments): createPortalLink auth tweaks (#420) Throw 'unauthenticated' code instead of 'failed-precondition' and use optional chaining (supported in Node 14) to avoid redundant check for 'context.auth.uid'. * docs(firestore-stripe-payments): Fix GitHub link to `firestore-stripe-web-sdk` README Fixes #409 * chore: 2022-08-23 release (#422) * [skip ci] Open PR * chore(firestore-stripe-payments): 0.3.0 release Co-authored-by: Darren Ackers <[email protected]> Co-authored-by: CJ Avilla <[email protected]> Co-authored-by: Allen <[email protected]> Co-authored-by: Will Battel <[email protected]>
1 parent 96b63ad commit fdb868c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+558
-170
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
runs-on: ubuntu-latest
1414
strategy:
1515
matrix:
16-
node: ["12", "14"]
16+
node: ["14", "16"]
1717
name: node.js_${{ matrix.node }}_test
1818
steps:
1919
- uses: actions/checkout@v2

codelab-steps/index.lab.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,6 @@ Check out some of these other extensions:
526526

527527
- [Check out the Cloud Functions for Firebase codelab](https://codelabs.developers.google.com/codelabs/firebase-cloud-functions/#0)
528528
- [Create Stripe customers and charge them on Firestore write](https://github.com/firebase/functions-samples/tree/master/stripe)
529-
- [Firebase mobile payments: Android & iOS with Cloud Functions for Firebase](https://github.com/stripe-samples/firebase-mobile-payments)
530529

531530
### Learn more about Stripe
532531

firestore-stripe-payments/CHANGELOG.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## Version 0.3.0 - 2022-08-23
2+
[feat] Allow configurable minimum instances for `createCheckoutSession` function. [#375]
3+
4+
[feat] Throw an `unauthenticated` Firebase error from `creatPortalLink` function. [#420]
5+
6+
[feat] Add Price object IDs from invoice line items for subscription payments to payments Firestore collection. [#393]
7+
18
## Version 0.2.7 - 2022-05-10
29
[chore] Updated package-lock.json that was causing install errors.
310

@@ -270,7 +277,7 @@ db.collection("products")
270277

271278
Previously, only subscriptions created via Stripe Checkout were synced to Cloud Firestore. By additionally listening to the `customer.subscription.created` event, the extension now also captures subscriptions created via the Stripe Dashboard or directly via the API. For this to work, Firebase Authentication users need to be synced with Stripe customer objects and the customers collection in Cloud Firestore (new configuration added in version `0.1.7`).
272279

273-
[docs] - Add snippet on importing Stripe.js as an ES module when using a build toolchain for your client application (e.g. Angular, React, TypeScript, etc.) to `POSTINSTALL.md`. (#74)
280+
- Add snippet on importing Stripe.js as an ES module when using a build toolchain for your client application (e.g. Angular, React, TypeScript, etc.) to `POSTINSTALL.md`. (#74)
274281

275282
## Version 0.1.6 - 2020-09-10
276283

@@ -393,7 +400,7 @@ type: "one_time" | "recurring";
393400

394401
[feat] - Add support for discounts, coupons, promotion codes:
395402

396-
You can create customer-facing promotion codes in the [Stripe Dashboard](https://dashboard.stripe.com/coupons/create). Refer to the [docs](https://stripe.com/docs/billing/subscriptions/discounts/codes) for a detailed guide on how to set these up.
403+
You can create customer-facing promotion codes in the [Stripe Dashboard](https://dashboard.stripe.com/coupons/create). Refer to the (https://stripe.com/docs/billing/subscriptions/discounts/codes) for a detailed guide on how to set these up.
397404

398405
To show the promotion code redemption box on the checkout page, set `allow_promotion_codes: true` when creating the `checkout_sessions` document:
399406

firestore-stripe-payments/POSTINSTALL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
### Client SDK
22

3-
You can use the [`@stripe/firestore-stripe-payments`](https://github.com/stripe/stripe-firebase-extensions/blob/web-sdk/firestore-stripe-web-sdk/README.md)
3+
You can use the [`@stripe/firestore-stripe-payments`](https://github.com/stripe/stripe-firebase-extensions/blob/next/firestore-stripe-web-sdk/README.md)
44
JavaScript package to easily access this extension from web clients. This client SDK provides
55
TypeScript type definitions and high-level convenience APIs for most common operations client
66
applications would want to implement using the extension.

firestore-stripe-payments/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Before installing this extension, set up the following Firebase services in your
4848

4949
Then, in the [Stripe Dashboard](https://dashboard.stripe.com):
5050

51-
- Create a new [restricted key](https://stripe.com/docs/keys#limit-access) with write access for the "Customers", "Checkout Sessions" and "Customer portal" resources, and read-only access for the "Subscriptions" and "Plans" resources.
51+
- Create a new [restricted key](https://stripe.com/docs/keys#limit-access) with write access for the "Customers", "Checkout Sessions" and "Customer portal" resources, and read-only access for the "Subscriptions" and "Prices" resources.
5252

5353
#### Billing
5454

firestore-stripe-payments/_emulator/extensions/firestore-stripe-payments.env.local

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
STRIPE_API_KEY=
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"extensions": {
3+
"firestore-stripe-payments": "../"
4+
},
5+
"storage": {
6+
"rules": "storage.rules"
7+
},
8+
"emulators": {
9+
"hub": {
10+
"port": 4000
11+
},
12+
"storage": {
13+
"port": 9199
14+
},
15+
"auth": {
16+
"port": 9099
17+
},
18+
"ui": {
19+
"enabled": true
20+
},
21+
"functions": {
22+
"port": 5001
23+
}
24+
},
25+
"firestore": {
26+
"rules": "firestore.rules",
27+
"indexes": "firestore.indexes.json"
28+
}
29+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"indexes": [],
3+
"fieldOverrides": []
4+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
service cloud.firestore {
2+
match /databases/{database}/documents {
3+
match /{document=**} {
4+
// This rule allows anyone with your database reference to view, edit,
5+
// and delete all data in your database. It is useful for getting
6+
// started, but it is configured to expire after 30 days because it
7+
// leaves your app open to attackers. At that time, all client
8+
// requests to your database will be denied.
9+
//
10+
// Make sure to write security rules for your app before that time, or
11+
// else all client requests to your database will be denied until you
12+
// update your rules.
13+
allow read, write: if request.time < timestamp.date(2022, 7, 2);
14+
}
15+
}
16+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
rules_version = '2';
2+
service firebase.storage {
3+
match /b/{bucket}/o {
4+
match /{allPaths=**} {
5+
allow read, write: if true;
6+
}
7+
}
8+
}

firestore-stripe-payments/extension.yaml

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414

1515
name: firestore-stripe-payments
16-
version: 0.2.7
16+
version: 0.3.0
1717
specVersion: v1beta
1818

1919
displayName: Run Payments with Stripe
@@ -212,8 +212,8 @@ params:
212212
- param: DELETE_STRIPE_CUSTOMERS
213213
label: Automatically delete Stripe customer objects
214214
description: >-
215-
Do you want to automatically delete customer objects in Stripe?
216-
When a user is deleted in Firebase Authentication or in Cloud Firestore and set to 'Auto delete'
215+
Do you want to automatically delete customer objects in Stripe?
216+
When a user is deleted in Firebase Authentication or in Cloud Firestore and set to 'Auto delete'
217217
the extension will delete their customer object in Stripe which will immediately cancel all subscriptions for the user.
218218
type: select
219219
options:
@@ -228,7 +228,7 @@ params:
228228
label: Stripe API key with restricted access
229229
type: secret
230230
description: >-
231-
What is your Stripe API key?
231+
What is your Stripe API key?
232232
We recommend creating a new [restricted key](https://stripe.com/docs/keys#limit-access)
233233
with write access only for the "Customers", "Checkout Sessions" and "Customer portal" resources.
234234
And read-only access for the "Subscriptions" and "Plans" resources.
@@ -239,13 +239,24 @@ params:
239239
label: Stripe webhook secret
240240
type: secret
241241
description: >-
242-
This is your signing secret for a Stripe-registered webhook.
242+
This is your signing secret for a Stripe-registered webhook.
243243
This webhook can only be registered after installation.
244-
Leave this value untouched during installation, then follow the
244+
Leave this value untouched during installation, then follow the
245245
postinstall instructions for registering your webhook
246246
and configuring this value.
247247
example: whsec_1234567890
248248
required: false
249+
250+
- param: CREATE_CHECKOUT_SESSION_MIN_INSTANCES
251+
label: Minimum instances for createCheckoutSession function
252+
type: secret
253+
description: >-
254+
Set the minimum number of function instances that should be always be available to create Checkout Sessions.
255+
This number can be adjusted to reduce cold starts and increase the responsiveness
256+
of Checkout Session creation requests. Suggested values are 0 or 1. Please note this setting will likely incur billing costss, see the [Firebase documentation](https://firebase.google.com/docs/functions/manage-functions#reduce_the_number_of_cold_starts) for more information.
257+
default: 0
258+
required: true
259+
249260
events:
250261
- type: com.stripe.v1.product.created
251262
description: Occurs whenever a product is created.
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import * as admin from 'firebase-admin';
2+
import { DocumentReference, DocumentData } from '@google-cloud/firestore';
3+
import {
4+
waitForDocumentToExistInCollection,
5+
waitForDocumentToExistWithField,
6+
} from './utils';
7+
import { UserRecord } from 'firebase-functions/v1/auth';
8+
import setupEmulator from './setupEmulator';
9+
10+
if (admin.apps.length === 0) {
11+
admin.initializeApp({ projectId: 'demo-project' });
12+
}
13+
14+
setupEmulator();
15+
16+
const firestore = admin.firestore();
17+
18+
function customerCollection() {
19+
return firestore.collection('customers');
20+
}
21+
22+
function paymentsCollection(userId) {
23+
return firestore.collection('customers').doc(userId).collection('payments');
24+
}
25+
26+
export async function findCustomerInCollection(user: UserRecord) {
27+
const doc = firestore.collection('customers').doc(user.uid);
28+
29+
const customerDoc = await waitForDocumentToExistWithField(
30+
doc,
31+
'stripeId',
32+
60000
33+
);
34+
35+
return Promise.resolve({ docId: user.uid, ...customerDoc.data() });
36+
}
37+
38+
export async function findCustomerPaymentInCollection(
39+
userId: string,
40+
stripeId: string
41+
) {
42+
const paymentDoc: DocumentData = await waitForDocumentToExistInCollection(
43+
paymentsCollection(userId),
44+
'customer',
45+
stripeId
46+
);
47+
48+
const paymentRef = paymentsCollection(userId).doc(paymentDoc.doc.id);
49+
50+
const updatedPaymentDoc = await waitForDocumentToExistWithField(
51+
paymentRef,
52+
'prices'
53+
);
54+
55+
return updatedPaymentDoc.data();
56+
}
57+
58+
export async function createCheckoutSession(userId, subscription) {
59+
const checkoutSessionCollection = customerCollection()
60+
.doc(userId)
61+
.collection('checkout_sessions');
62+
63+
const checkoutSessionDocument: DocumentReference =
64+
await checkoutSessionCollection.add({
65+
success_url: 'http://test.com/success',
66+
cancel_url: 'http://test.com/cancel',
67+
...subscription,
68+
});
69+
70+
const checkoutSessionDoc = await waitForDocumentToExistWithField(
71+
checkoutSessionDocument,
72+
'created'
73+
);
74+
75+
return checkoutSessionDoc.data();
76+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import puppeteer from 'puppeteer';
2+
3+
export default async (url: string): Promise<void> => {
4+
console.info('Running checkout form in headless mode...');
5+
const browser = await puppeteer.launch();
6+
try {
7+
const page = await browser.newPage();
8+
await page.goto(url, {
9+
waitUntil: 'networkidle0',
10+
timeout: 120000,
11+
});
12+
13+
await page.focus('#cardNumber');
14+
await page.keyboard.type('4242424242424242', { delay: 100 });
15+
await page.keyboard.press('Enter');
16+
17+
await page.focus('#cardExpiry');
18+
await page.keyboard.type('1224');
19+
20+
await page.focus('#cardCvc');
21+
await page.keyboard.type('123');
22+
23+
await page.focus('#billingName');
24+
await page.keyboard.type('testing');
25+
26+
await page.focus('#billingAddressLine1');
27+
await page.keyboard.type('1600 Amphitheatre Parkwa');
28+
await page.keyboard.press('Enter');
29+
30+
await page.focus('#billingLocality');
31+
await page.keyboard.type('Mountain View');
32+
33+
await page.focus('#billingPostalCode');
34+
await page.keyboard.type('CA 94043');
35+
await page.keyboard.press('Enter');
36+
37+
await page.waitForNetworkIdle();
38+
await browser.close();
39+
} catch (exception) {
40+
} finally {
41+
await browser.close();
42+
}
43+
};
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const path = require('path');
2+
3+
export const pathToenvFile = path.resolve(
4+
__dirname,
5+
'../../../_emulator/extensions/firestore-stripe-payments.env.local'
6+
);
7+
8+
export const pathTosecretsFile = path.resolve(
9+
__dirname,
10+
'../../../_emulator/extensions/firestore-stripe-payments.secret.local'
11+
);
12+
13+
export const setupEnvironment = () => {
14+
require('dotenv').config({
15+
path: pathToenvFile,
16+
});
17+
18+
require('dotenv').config({
19+
path: pathTosecretsFile,
20+
});
21+
};

0 commit comments

Comments
 (0)