0

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:

enter image description here

NCT 127
  • 596
  • 1
  • 6
  • 27

1 Answers1

1

I've not tried this but as per the typescript hint. this function accepts 2 arguments.

  1. arg0 of type object which has property error of type any.
  2. arg1 of type object which has property merge of type boolean or undefined.

So you need to call it something like this if setting error.

 snap.ref.set({ error: new Error("Error") }, { merge: true }); // second arg can be merge as true/false or undefined

if not setting error

snap.ref.set({ error: null }, { merge: true }); // second arg can be merge as true/false or undefined

snap.ref.set({ error: null }, undefined);
  • I have this line: set({ error: new Error("Error") }, { merge: true });//set(payment) as u can see the payment data is commented out. Hoow do I include it in the call? Couold u show as an example above? – NCT 127 Mar 22 '21 at 03:57
  • What is the value of `paymentMethod` which is passed into `await snap.ref.set(paymentMethod);` method, couldn't see it in the screenshot. If it's like `const paymentMethod = { arg1: { error: new Error("Error") }, arg2: { merge: true }}` then use it like this `await snap.ref.set(paymentMethod.arg1, paymentMethod.arg2);`, else you can directly pass the object itself like i showed above `snap.ref.set({ error: new Error("Error") }, { merge: true });` – Rajesh Kumar Mar 22 '21 at 05:49
  • its in the code in the question but here it is to make easy: await stripe.paymentIntents.create( { amount, currency, customer, payment_method, off_session: false, confirm: true, confirmation_method: 'manual', }, { idempotencyKey } ) Edit: I think any – NCT 127 Mar 22 '21 at 05:50
  • Ok sorry, I see what you mean now. – NCT 127 Mar 22 '21 at 05:52
  • I see in the code now, can you try this `await snap.ref.set(...payment);` – Rajesh Kumar Mar 22 '21 at 05:53