Hello i am having trouble sending a paymentmethod parameter to php function (createSubscription) in order to create a subscription on Stripe for my users.
import { Component, AfterViewInit, ElementRef, ViewChild } from '@angular/core';
import { Router } from "@angular/router";
import { UserData } from 'src/app/services/user-data/user-data';
import {
loadStripe,
Stripe,
StripeElements,
StripeCardElement,
StripeElementsOptions,
StripeElement,
StripeCardNumberElement,
PaymentMethod,
StripeCardElementChangeEvent,
} from '@stripe/stripe-js';
class CustomStripeCardElement {
private stripeCardElement: StripeCardElement;
paymentMethod: string | PaymentMethod;
constructor(stripeCardElement: StripeCardElement) {
this.stripeCardElement = stripeCardElement;
}
on(
eventType: 'change' | 'ready' | 'focus' | 'blur' | 'escape' | 'networkschange',
handler: (event: StripeCardElementChangeEvent | {}) => any
): void {
(this.stripeCardElement.on as any)(eventType, handler);
}
mount(selector: string): void {
this.stripeCardElement.mount(selector);
}
}
@Component({
selector: 'app-subscription',
templateUrl: 'subscription.page.html',
styleUrls: ['subscription.page.scss'],
})
export class SubscriptionPage implements AfterViewInit {
@ViewChild('paymentElement') paymentElementRef: ElementRef;
stripe: Stripe;
elements: StripeElements;
paymentElement: StripeCardElement | StripeCardNumberElement | CustomStripeCardElement;
clientSecret: string;
planId = 'price_1OPuphGozbMWFnurmxxxxxx';
customerId: any;
isPaymentElementFilled: boolean = false;
appearance: StripeElementsOptions['appearance'] = {
theme: 'stripe',
variables: {
colorPrimary: '#0570de',
colorBackground: '#ffffff',
colorText: '#30313d',
colorDanger: '#df1b41',
fontFamily: 'Ideal Sans, system-ui, sans-serif',
spacingUnit: '2px',
borderRadius: '4px',
// Define other custom styling variables as needed
},
};
menuOpenCopyright: boolean = false;
user: any = {};
email: string = '';
uid: string = '';
modalOpen: boolean = false;
cardInputFocused: boolean = false;
paymentMethod: string | PaymentMethod;
constructor(
public router: Router,
public userData: UserData,
) {
this.user = this.userData.getUserData();
this.uid = this.user.uid;
this.email = this.user.email;
this.clientSecret
}
ngAfterViewInit() {
this.userData.createCustomer(this.email).subscribe((customerResponse: any) => {
if (customerResponse.success) {
this.customerId = customerResponse.customer.id;
console.log('Customer created:', customerResponse.customer);
this.userData.createSetupIntent(this.customerId).subscribe((setupIntentResponse: any) => {
if (setupIntentResponse.success) {
this.clientSecret = setupIntentResponse.setupIntent.client_secret;
// Initialize Stripe after getting clientSecret
this.initializeStripe().then(() => {
// Once Stripe is initialized, create the payment element
this.createPaymentElement(this.clientSecret);
// Create Google Pay button after initializing Stripe
this.createGooglePayButton();
}).catch(error => {
console.error('Error initializing Stripe:', error);
});
} else {
console.error(setupIntentResponse.error);
}
});
} else {
console.error(customerResponse.error);
}
});
}
async initializeStripe() {
const stripePromise = loadStripe('pk_test_51HiSUoGozbMWFnurWW3azSXFZV47mcnH8p4MQG6HvbHuszrDPvUFYuq15TbVqZujcJNv4CSHSqkAkFm3pRk7nwoxxxxxxxx');
this.stripe = await stripePromise;
}
createPaymentElement(clientSecret) {
if (!this.stripe) {
console.error('Stripe is not initialized.');
return;
}
this.elements = this.stripe.elements({
clientSecret: clientSecret,
});
const paymentElementOptions = {
};
const stripeCardElement = this.elements.create(
'payment',
paymentElementOptions
) as unknown as StripeCardElement;
this.paymentElement = new CustomStripeCardElement(stripeCardElement);
this.paymentElement.on('change', (event: any) => {
this.isPaymentElementFilled = event.complete;
});
this.paymentElement.on('networkschange', (event: any) => {
});
this.paymentElement.mount('#payment-element');
}
async confirmPayment() {
const { setupIntent, error } = await this.stripe.confirmCardSetup(this.clientSecret, {
payment_method: {
card: this.paymentElement as StripeCardElement,
billing_details: {
email: this.email,
},
},
});
console.log('Payment Method:', setupIntent.payment_method );
if (error) {
console.error(error);
} else if (setupIntent.status === 'succeeded') {
console.log('Payment succeeded:', setupIntent);
const paymentMethod = setupIntent.payment_method;
this.paymentMethod = paymentMethod;
console.log('Payment Method:', this.paymentMethod );
this.subscribe(paymentMethod);
}
}
async subscribe(paymentMethod: string | PaymentMethod) {
const paymentMethodString = paymentMethod as string;
console.log(paymentMethodString);
this.userData.createSubscription(this.customerId, this.planId, this.clientSecret, paymentMethodString).subscribe((subscriptionResponse: any) => {
if (subscriptionResponse.success) {
console.log('Subscription successful:', subscriptionResponse.subscription);
} else {
console.error(subscriptionResponse.error);
}
});
}
user-data services
createCustomer(email: string) {
const url = this.appData.getApiUrl() + 'createCustomer';
const data = this.jsonToURLEncoded({
api_signature: this.api_signature,
email: email
});
return this.http.post(url, data, { headers: this.options });
}
createSetupIntent(customerId: string) {
const url = this.appData.getApiUrl() + 'createSetupIntent';
const data = this.jsonToURLEncoded({
customerId: customerId
});
return this.http.post(url, data, { headers: this.options });
};
createSubscription(customerId: string, planId: string, clientSecret: string, paymentMethod: string) {
const url = this.appData.getApiUrl() + 'createSubscription';
const data = this.jsonToURLEncoded({
customerId: customerId,
planId: planId,
clientSecret: clientSecret,
paymentMethod: paymentMethod, // Add the paymentMethod parameter here
});
return this.http.post(url, data, { headers: this.options });
}
and php functions
function createCustomer() {
$request = SlimSlim::getInstance()->request();
$response['success'] = true;
$userEmail = $request->post('email');
try {
$customer = StripeCustomer::create([
'email' => $userEmail,
]);
$userStripeCustomerId = $customer->id;
$db = getDB();
$sql = "UPDATE users SET customer_id = :customer_id WHERE email = :email";
$stmt = $db->prepare($sql);
$stmt->bindParam(":customer_id", $userStripeCustomerId, PDO::PARAM_STR);
$stmt->bindParam(":email", $userEmail, PDO::PARAM_STR);
$stmt->execute();
$response = [
'customer' => $customer,
'success' => true,
];
} catch (StripeExceptionCardException $e) {
$response = [
'error' => $e->getMessage(),
'success' => false,
];
}
echo json_encode($response);
}
function createSetupIntent() {
$request = SlimSlim::getInstance()->request();
$response['success'] = true;
$customerId = $request->post('customerId');
try {
$setupIntent = StripeSetupIntent::create([
'customer' => $customerId,
]);
$clientSecret = $setupIntent->client_secret;
$response = [
'setupIntent' => $setupIntent,
'clientSecret' => $clientSecret,
'success' => true,
];
} catch (StripeExceptionCardException $e) {
$response = [
'error' => $e->getMessage(),
'success' => false,
];
}
echo json_encode($response);
}
function createSubscription() {
$request = SlimSlim::getInstance()->request();
$response['success'] = true;
$customerId = $request->post('customerId');
$planId = $request->post('planId');
$paymentMethod = $request->post('paymentMethod');
try {
$subscription = StripeSubscription::create([
'customer' => $customerId,
'items' => [
[
'price' => $planId,
],
],
'default_payment_method' => $paymentMethod,
]);
$response = [
'subscription' => $subscription,
'success' => true,
];
} catch (StripeExceptionCardException $e) {
$response = [
'error' => $e->getMessage(),
'success' => false,
];
}
echo json_encode($response);
}
it doesnt send the paymentmethod to php function createSubscription on payload i am getting three values that are being send
customerId: cus_QR72U2UAixxxx
planId: price_1OPuphGozbMWFnurmxxxxxx
clientSecret: seti_1PaEoKGozbMWFnurhsTnE8O2_secret_QR72n7s1kxxxxxxxxxx
and i am getting this error Slim Application Error
The application could not run because of the following error:
Details
Type: StripeExceptionInvalidRequestException
Message: This customer has no attached payment source or default payment method. Please consider adding a default payment method. For more information, visit https://stripe.com/docs/billing/subscriptions/payment-methods-setting#payment-method-priority.
File: public_html/api/vendor/stripe/stripe-php/lib/Exception/ApiErrorException.php
Line: 38
Trace
#0 public_html/api/vendor/stripe/stripe-php/lib/Exception/InvalidRequestException.php(35): StripeExceptionApiErrorException::factory('This customer h...', 400, '{n "error": {n...', Array, Object(StripeUtilCaseInsensitiveArray), 'resource_missin...')
#1 /public_html/api/vendor/stripe/stripe-php/lib/ApiRequestor.php(189): StripeExceptionInvalidRequestException::factory('This customer h...', 400, '{n "error": {n...', Array, Object(StripeUtilCaseInsensitiveArray), 'resource_missin...', NULL)
#2 public_html/api/vendor/stripe/stripe-php/lib/ApiRequestor.php(151): StripeApiRequestor::_specificAPIError('{n "error": {n...', 400, Object(StripeUtilCaseInsensitiveArray), Array, Array)
#3 public_html/api/vendor/stripe/stripe-php/lib/ApiRequestor.php(478): StripeApiRequestor->handleErrorResponse('{n "error": {n...', 400, Object(StripeUtilCaseInsensitiveArray), Array)
#4 /public_html/api/vendor/stripe/stripe-php/lib/ApiRequestor.php(120): StripeApiRequestor->_interpretResponse('{n "error": {n...', 400, Object(StripeUtilCaseInsensitiveArray))
#5 /public_html//api/vendor/stripe/stripe-php/lib/ApiOperations/Request.php(63): StripeApiRequestor->request('post', '/v1/subscriptio...', Array, Array)
#6 public_htmlapi/vendor/stripe/stripe-php/lib/ApiOperations/Create.php(25): StripeApiResource::_staticRequest('post', '/v1/subscriptio...', Array, NULL)
The customer is created just fine, The other php functions are working ok. I am not getting any other erros on console or the console.logs on frontends functions confirmPayment or subscribe, only the error for bad request 500. Can you tell me how to send payment method correctly? Or is it another thing that i am missing? Thanks