I've copied the firebase stripe GitHub example index.js and have been fixing the errors which pop up from hosting the code in my index.ts (typescript) file. The error I have left is the following:
Expected 2 arguments, but got 1.ts(2554) index.ts(54, 112): An argument for 'arg1' was not provided.
It's about this line (there are multiple instances although this should not matter to the solution):
await snap.ref.set(paymentMethod);
How do I fix this error? I have come across this question, but it did not solve my problem.
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const { Logging } = require('@google-cloud/logging');
const logging = new Logging({
projectId: process.env.GCLOUD_PROJECT,
});
const { Stripe } = require('stripe');
const stripe = new Stripe(functions.config().stripe.secret, {
apiVersion: '2020-08-27',
});
/**
* When a user is created, create a Stripe customer object for them.
*
* @see https://stripe.com/docs/payments/save-and-reuse#web-create-customer
*/
exports.createStripeCustomer = functions.auth.user().onCreate(async (user: { email: any; uid: any; }) => {
const customer = await stripe.customers.create({ email: user.email });
const intent = await stripe.setupIntents.create({
customer: customer.id,
});
await admin.firestore().collection('stripe_customers').doc(user.uid).set({
customer_id: customer.id,
setup_secret: intent.client_secret,
});
return;
});
/**
* When adding the payment method ID on the client,
* this function is triggered to retrieve the payment method details.
*/
exports.addPaymentMethodDetails = functions.firestore
.document('/stripe_customers/{userId}/payment_methods/{pushId}')
.onCreate(async (snap: { data: () => { (): any; new(): any; id: any; }; ref: { set: (arg0: { error: any; }, arg1: { merge: boolean; } | undefined) => any; parent: { parent: { set: (arg0: { setup_secret: any; }, arg1: { merge: boolean; }) => any; }; }; }; }, context: { params: { userId: any; }; }) => {
try {
const paymentMethodId = snap.data().id;
const paymentMethod = await stripe.paymentMethods.retrieve(
paymentMethodId
);
await snap.ref.set(paymentMethod);
// Create a new SetupIntent so the customer can add a new method next time.
const intent = await stripe.setupIntents.create({
customer: `${paymentMethod.customer}`,
});
await snap.ref.parent.parent.set(
{
setup_secret: intent.client_secret,
},
{ merge: true }
);
return;
} catch (error) {
await snap.ref.set({ error: userFacingMessage(error) }, { merge: true });
await reportError(error, { user: context.params.userId });
}
});
/**
* When a payment document is written on the client,
* this function is triggered to create the payment in Stripe.
*
* @see https://stripe.com/docs/payments/save-and-reuse#web-create-payment-intent-off-session
*/
// [START chargecustomer]
exports.createStripePayment = functions.firestore
.document('stripe_customers/{userId}/payments/{pushId}')
.onCreate(async (snap: { data: () => { amount: any; currency: any; payment_method: any; }; ref: { parent: { parent: { get: () => any; }; }; set: (arg0: { error: any; }, arg1: { merge: boolean; } | undefined) => any; }; }, context: { params: { pushId: any; userId: any; }; }) => {
const { amount, currency, payment_method } = snap.data();
try {
// Look up the Stripe customer id.
const customer = (await snap.ref.parent.parent.get()).data().customer_id;
// Create a charge using the pushId as the idempotency key
// to protect against double charges.
const idempotencyKey = context.params.pushId;
const payment = await stripe.paymentIntents.create(
{
amount,
currency,
customer,
payment_method,
off_session: false,
confirm: true,
confirmation_method: 'manual',
},
{ idempotencyKey }
);
// If the result is successful, write it back to the database.
await snap.ref.set(payment);
} catch (error) {
// We want to capture errors and render them in a user-friendly way, while
// still logging an exception with StackDriver
console.log(error);
await snap.ref.set({ error: userFacingMessage(error) }, { merge: true });
await reportError(error, { user: context.params.userId });
}
});
// [END chargecustomer]
/**
* When 3D Secure is performed, we need to reconfirm the payment
* after authentication has been performed.
*
* @see https://stripe.com/docs/payments/accept-a-payment-synchronously#web-confirm-payment
*/
exports.confirmStripePayment = functions.firestore
.document('stripe_customers/{userId}/payments/{pushId}')
.onUpdate(async (change: { after: { data: () => { (): any; new(): any; status: string; id: any; }; ref: { set: (arg0: any) => void; }; }; }, context: any) => {
if (change.after.data().status === 'requires_confirmation') {
const payment = await stripe.paymentIntents.confirm(
change.after.data().id
);
change.after.ref.set(payment);
}
});
/**
* When a user deletes their account, clean up after them
*/
exports.cleanupUser = functions.auth.user().onDelete(async (user: { uid: any; }) => {
const dbRef = admin.firestore().collection('stripe_customers');
const customer = (await dbRef.doc(user.uid).get()).data();
await stripe.customers.del(customer.customer_id);
// Delete the customers payments & payment methods in firestore.
const batch = admin.firestore().batch();
const paymetsMethodsSnapshot = await dbRef
.doc(user.uid)
.collection('payment_methods')
.get();
paymetsMethodsSnapshot.forEach((snap: { ref: any; }) => batch.delete(snap.ref));
const paymentsSnapshot = await dbRef
.doc(user.uid)
.collection('payments')
.get();
paymentsSnapshot.forEach((snap: { ref: any; }) => batch.delete(snap.ref));
await batch.commit();
await dbRef.doc(user.uid).delete();
return;
});
/**
* To keep on top of errors, we should raise a verbose error report with Stackdriver rather
* than simply relying on console.error. This will calculate users affected + send you email
* alerts, if you've opted into receiving them.
*/
// [START reporterror]
function reportError(err: { stack: any; }, context = {}) {
// This is the name of the StackDriver log stream that will receive the log
// entry. This name can be any valid log stream name, but must contain "err"
// in order for the error to be picked up by StackDriver Error Reporting.
const logName = 'errors';
const log = logging.log(logName);
// https://cloud.google.com/logging/docs/api/ref_v2beta1/rest/v2beta1/MonitoredResource
const metadata = {
resource: {
type: 'cloud_function',
labels: { function_name: process.env.FUNCTION_NAME },
},
};
// https://cloud.google.com/error-reporting/reference/rest/v1beta1/ErrorEvent
const errorEvent = {
message: err.stack,
serviceContext: {
service: process.env.FUNCTION_NAME,
resourceType: 'cloud_function',
},
context: context,
};
// Write the error log entry
return new Promise<void>((resolve, reject) => {
log.write(log.entry(metadata, errorEvent), (error: any) => {
if (error) {
return reject(error);
}
return resolve();
});
});
}
// [END reporterror]
/**
* Sanitize the error message for the user.
*/
function userFacingMessage(error: { type: any; message: any; }) {
return error.type
? error.message
: 'An error occurred, developers have been alerted';
}
Full error: