ERROR: "A source must be attached to a customer to be used as a `payment_method`."

2,885 views
Skip to first unread message

Alon Azar

unread,
Feb 6, 2020, 5:40:19 PM2/6/20
to Stripe API Discussion
I am using the Stripe.net library to access the API. I get an error when trying to create a customer object after the charging the card. See sample code. There is little information regarding this error. Am I missing somethig?

        [HttpPost("charge", Name = "Charge")]
        public IActionResult Charge(PaymentDto payment)
        {
            StripeConfiguration.ApiKey = _stripeConfiguration.SecretKey;

            var token = payment.StripeToken;

            var chargeCreateOptions = new ChargeCreateOptions
            {
                Amount = 999,
                Currency = "cad",
                Description = "Product Subscription",
                Source = token,
                Capture = false,
                ReceiptEmail = "my-e...@my-domain.com",
            };

            // For this exercise, assume everything succeeded
            var chargeService = new ChargeService();
            var charge = chargeService.Create(chargeCreateOptions);


            // Do stuff... For this exercise, assume everything succeeded


            var chargeCaptureOptions = new ChargeCaptureOptions
            {
                ReceiptEmail = "my-e...@my-domain.com",
            };

            // Capture the funds. For this exercise, assume everything succeeded
            var capture = chargeService.Capture(charge.Id, chargeCaptureOptions);

            // Retrieve the payment method (not a necessary call but illustrates the issue)
            var paymentMethodService = new PaymentMethodService();
            var paymentMethod = paymentMethodService.Get(capture.PaymentMethodId); // <-- ERROR: "A source must be attached to a customer to be used as a `payment_method`."


            // Create customer for later charges
            var custOptions = new CustomerCreateOptions
            {
                PaymentMethod = capture.PaymentMethodId,
                Email = "my-e...@my-domain.com",
            };

            var custService = new CustomerService();
            var customer = custService.Create(custOptions); // <-- ERROR: "A source must be attached to a customer to be used as a `payment_method`."

            // Do stuff...

            return View();
       }



Remi J.

unread,
Feb 6, 2020, 5:53:02 PM2/6/20
to api-d...@lists.stripe.com
Hello Alon,

Thanks for sending the code you're using as it makes it a lot easier to understand what is going wrong.

PaymentMethods are a new API resource that we introduced a while ago to represent payment methods such as Card or SEPA Debit. Most of those objects have an id that looks like this: pm_123. PaymentMethods are used with PaymentIntent and SetupIntents in our API and are not compatible with Charges.

PaymentMethods are a newer abstraction that will replace legacy objects such as Source, Card or BankAccount. Legacy integrations have those existing objects though so we wanted to make it easy to migrate to PaymentMethods. For that reason, existing reusable Cards or Sources can be actively fetched as a PaymentMethod. They work exactly like a normal PaymentMethod in the API. This compatibility layer is only built for reusable objects which means you would have saved the card on a Customer before charging.

Your code today, uses the Charge API to charge a customer. When you're charging that customer's card, you're passing a Token id (tok_123) in the `source` parameter. Doing this creates a one-time charge and consumes the token entirely. That card token can not be re-used moving forward. If you wanted to save that card and be able to re-use it later, you would need to first create the customer and save the card with the Create Card API (https://stripe.com/docs/api/cards/create). This would pass the token id (tok_123) and return a card id (card_123) back. After that, you can charge that saved card by passing the customer id in the `customer` parameter and the card id in the `source` parameter.

Also, if you're just starting, I would recommend not using the Charge API at all. Instead, you should use PaymentIntents from the start to accept payments. Doing this, you will ensure to only use PaymentMethods and never care about Token, Card or Source objects. You can read a lot more about this here: https://stripe.com/docs/payments/accept-a-payment#web

If you have any follow up questions, I'd recommend reaching out to our support team directly and they can help provide more context: https://support.stripe.com/contact

Best,
Remi

--
To unsubscribe from this group and stop receiving emails from it, send an email to api-discuss...@lists.stripe.com.

Alon Azar

unread,
Feb 7, 2020, 10:23:26 PM2/7/20
to api-d...@lists.stripe.com
Thank you, this helps a lot.

Cheers,
Alon

Alon Azar

unread,
Feb 7, 2020, 10:23:31 PM2/7/20
to Stripe API Discussion
That solved my problem and gave me some insight into the Stripe API internals. Thank you Remi.


On Thursday, February 6, 2020 at 2:53:02 PM UTC-8, Remi J. wrote:
Hello Alon,

Thanks for sending the code you're using as it makes it a lot easier to understand what is going wrong.

PaymentMethods are a new API resource that we introduced a while ago to represent payment methods such as Card or SEPA Debit. Most of those objects have an id that looks like this: pm_123. PaymentMethods are used with PaymentIntent and SetupIntents in our API and are not compatible with Charges.

PaymentMethods are a newer abstraction that will replace legacy objects such as Source, Card or BankAccount. Legacy integrations have those existing objects though so we wanted to make it easy to migrate to PaymentMethods. For that reason, existing reusable Cards or Sources can be actively fetched as a PaymentMethod. They work exactly like a normal PaymentMethod in the API. This compatibility layer is only built for reusable objects which means you would have saved the card on a Customer before charging.

Your code today, uses the Charge API to charge a customer. When you're charging that customer's card, you're passing a Token id (tok_123) in the `source` parameter. Doing this creates a one-time charge and consumes the token entirely. That card token can not be re-used moving forward. If you wanted to save that card and be able to re-use it later, you would need to first create the customer and save the card with the Create Card API (https://stripe.com/docs/api/cards/create). This would pass the token id (tok_123) and return a card id (card_123) back. After that, you can charge that saved card by passing the customer id in the `customer` parameter and the card id in the `source` parameter.

Also, if you're just starting, I would recommend not using the Charge API at all. Instead, you should use PaymentIntents from the start to accept payments. Doing this, you will ensure to only use PaymentMethods and never care about Token, Card or Source objects. You can read a lot more about this here: https://stripe.com/docs/payments/accept-a-payment#web

If you have any follow up questions, I'd recommend reaching out to our support team directly and they can help provide more context: https://support.stripe.com/contact

Best,
Remi

To unsubscribe from this group and stop receiving emails from it, send an email to api-d...@lists.stripe.com.
Reply all
Reply to author
Forward
0 new messages