Hello i am having a problem with Stripe Payment from buyer. I want the buyer to be able to buy digital content from my platform and we keep a fee from the buyer. I want the purchase to be done in the backend. I use stripe in another occasion and it is implemented ok. The seller account is created just fine and seller is verified from Stripe platform. The payment from buyer is succeed at first and then failed. As i show you from my stripe test dashboard in the photo i shared i think the payment intent returns incomplete. [![enter image description here][1]][1] THE CODE FRONT END async initializePaymentElement() { try { // Collect payment details const paymentDetails = { amount: this.fractionsToPurchase * this.pricePerFraction * 100, // Amount in smallest currency unit (e.g., cents) currency: 'eur', email: this.user.email, // User email seller_connected_account_id: this.seller_connected_account_id, // Seller's Stripe account ID }; console.log('Payment details being sent to backend:', paymentDetails); // Call backend to create Payment Intent const response: any = await this.userData.createPaymentIntent(paymentDetails).toPromise(); console.log('Response from backend (createPaymentIntent):', response); if (response && response.clientSecret) { this.clientSecret = response.clientSecret; this.buyer_customer_id = response.customerId; console.log('Client secret received:', this.clientSecret); console.log('Buyer customer ID:', this.buyer_customer_id); // Load Stripe.js and initialize payment element if (!this.stripe) { this.stripe = await loadStripe('your-publishable-key'); } if (!this.stripe) { console.error('Stripe.js failed to load'); return; } this.elements = this.stripe.elements({ clientSecret: this.clientSecret }); this.paymentElement = this.elements.create('payment'); // Mount Payment Element const paymentElementContainer = document.getElementById('payment-element'); if (paymentElementContainer) { this.paymentElement.mount(paymentElementContainer); } // Handle changes in the Payment Element this.paymentElement.on('change', (event: any) => { this.isPaymentElementFilled = event.complete; this.paymentError = event.error ? event.error.message : null; }); } else { console.error('Failed to retrieve client secret or initialize Stripe.'); this.paymentError = 'Failed to retrieve payment details.'; } } catch (error) { console.error('Error during initializePaymentElement:', error); this.paymentError = 'Failed to initialize payment. Please try again.'; } } async purchaseMediaFractions() { console.log("Starting purchaseMediaFractions..."); // Validate `fractionsToPurchase` if ( this.fractionsToPurchase > this.priceDetails?.fractions || this.fractionsToPurchase < 1 ) { console.error("Invalid fractions:", this.fractionsToPurchase); const toast = await this.toastController.create({ message: "Please enter a valid number of fractions to purchase.", duration: 2000, color: "danger", }); await toast.present(); return; } const totalPrice = this.fractionsToPurchase * this.pricePerFraction; const platformFee = totalPrice * 0.1; // 10% platform fee const sellerEarnings = totalPrice - platformFee; if (!this.stripe) { console.error("Stripe instance is not initialized."); return; } const elements = this.elements; if (!elements) { console.error("Stripe Elements are not initialized."); return; } const paymentElement = this.paymentElement; if (!paymentElement) { console.error("Payment element is not mounted."); return; } try { // Confirm the payment with Stripe const { error, paymentIntent } = await this.stripe.confirmPayment({ elements: this.elements, confirmParams: { payment_method_data: { billing_details: { email: this.user?.email, // Provide the buyer's email }, }, }, redirect: "if_required", // Handle the redirect manually }); if (error) { console.error("Payment confirmation error:", error.message); const toast = await this.toastController.create({ message: `Payment failed: ${error.message}`, duration: 3000, color: "danger", }); await toast.present(); return; } if (paymentIntent?.status === "succeeded") { console.log("Payment successful:", paymentIntent); const toast = await this.toastController.create({ message: "Payment successful!", duration: 3000, color: "success", }); await toast.present(); // Prepare purchase details for backend const purchaseDetails = { userId: this.user?.uid, mediaId: this.media?.msg_id, fractionsToPurchase: this.fractionsToPurchase, pricePerFraction: this.pricePerFraction, totalPrice, platformFee, sellerEarnings, sellerAccountId: this.seller_connected_account_id, buyerCustomerId: this.buyer_customer_id, }; console.log("Purchase details:", purchaseDetails); // Call the backend this.userData.purchaseMediaFractions(purchaseDetails).subscribe( async (response: any) => { console.log("Backend response for purchaseMediaFractions:", response); const toast = await this.toastController.create({ message: response.success ? response.message : response.error, duration: 2000, color: response.success ? "success" : "danger", }); await toast.present(); if (response.success) { console.log("Purchase completed successfully."); this.router.navigate(["/success"]); } else { console.error("Purchase failed:", response.error); } }, async (error) => { console.error("HTTP error in purchaseMediaFractions:", error); const toast = await this.toastController.create({ message: "An error occurred. Please try again later.", duration: 2000, color: "danger", }); await toast.present(); } ); } else { console.error("Payment not completed:", paymentIntent?.status); const toast = await this.toastController.create({ message: "Payment not completed.", duration: 3000, color: "warning", }); await toast.present(); } } catch (error) { console.error("Error during payment process:", error); const toast = await this.toastController.create({ message: "An error occurred during payment. Please try again later.", duration: 3000, color: "danger", }); await toast.present(); } } Services userData createPaymentIntent(paymentDetails: any) { const url = this.appData.getApiUrl() + 'createPaymentIntent'; const data = this.jsonToURLEncoded({ api_signature: this.api_signature, ...paymentDetails, // Spread payment details into the request body }); console.log('Calling createPaymentIntent API:', url); console.log('Request data:', data); return this.http.post(url, data, { headers: this.options }).pipe( tap((response: any) => { console.log('createPaymentIntent API response:', response); }), catchError((error) => { console.error('Error calling createPaymentIntent API:', error); throw error; }) ); } purchaseMediaFractions(purchaseDetails: any) { const url = this.appData.getApiUrl() + 'insertMediaPurchaseDetails'; const data = { api_signature: this.api_signature, purchaseDetails: purchaseDetails // Send as a plain object }; return this.http.post(url, JSON.stringify(data), { headers: this.options.set('Content-Type', 'application/json'), }); } AND PHP FUNCTIONS function createPaymentIntent() { $request = \Slim\Slim::getInstance()->request(); $response = ['success' => false]; // Extract parameters sent from the frontend $apiSignature = $request->post('api_signature'); $amount = intval($request->post('amount')); // Ensure amount is an integer $currency = $request->post('currency'); $email = $request->post('email'); $sellerAccountId = $request->post('seller_connected_account_id'); // Seller's connected account ID error_log("Received API Signature: $apiSignature, Amount: $amount, Currency: $currency, Email: $email, Seller Account ID: $sellerAccountId"); try { // Validate parameters if (!$amount || $amount <= 0) { throw new Exception("Invalid amount: Amount must be greater than 0."); } if (empty($email)) { throw new Exception("Invalid email: Email address is required."); } if (empty($sellerAccountId)) { throw new Exception("Invalid seller account ID: This is required."); } // Create a new Stripe Customer $customer = \Stripe\Customer::create([ 'email' => $email, 'description' => 'One-time customer for purchase', ]); error_log("Stripe Customer Created: " . json_encode($customer)); $buyerCustomerId = $customer->id; if (empty($buyerCustomerId)) { throw new Exception("Failed to create a Stripe customer."); } // Calculate Platform Fee (e.g., 10%) $applicationFeeAmount = intval($amount * 0.10); // Platform fee // Create the PaymentIntent $paymentIntentParams = [ 'amount' => $amount, // Amount in smallest currency unit (e.g., cents) 'currency' => $currency, 'customer' => $buyerCustomerId, 'description' => 'Purchase', 'transfer_data' => [ 'destination' => $sellerAccountId, // Connected seller account ], 'application_fee_amount' => $applicationFeeAmount, ]; error_log("PaymentIntent Parameters: " . json_encode($paymentIntentParams)); $paymentIntent = \Stripe\PaymentIntent::create($paymentIntentParams); error_log("PaymentIntent Created: " . json_encode($paymentIntent)); // Build the response with PaymentIntent details $response = [ 'success' => true, 'paymentIntentId' => $paymentIntent->id, 'clientSecret' => $paymentIntent->client_secret, 'customerId' => $buyerCustomerId, 'amount' => $amount, 'currency' => $currency, ]; } catch (\Stripe\Exception\ApiErrorException $e) { // Stripe-specific error error_log("Stripe API Error: " . $e->getMessage()); $response = [ 'success' => false, 'error' => $e->getMessage(), ]; } catch (Exception $e) { // General error error_log("General Error: " . $e->getMessage()); $response = [ 'success' => false, 'error' => $e->getMessage(), ]; } // Return response as JSON echo json_encode($response); } function insertMediaPurchaseDetails() { error_log('Function Called: insertMediaPurchaseDetails'); $request = \Slim\Slim::getInstance()->request(); $data = json_decode($request->getBody(), true); $purchaseDetails = $data['purchaseDetails'] ?? null; error_log('Request Body: ' . $request->getBody()); // Validate purchase details if (!$purchaseDetails || !isset($purchaseDetails['userId'], $purchaseDetails['mediaId'], $purchaseDetails['fractionsToPurchase'], $purchaseDetails['pricePerFraction'], $purchaseDetails['totalPrice'], $purchaseDetails['platformFee'], $purchaseDetails['sellerEarnings'])) { error_log('Invalid Purchase Details: ' . print_r($purchaseDetails, true)); echo json_encode(['error' => 'Invalid purchase details provided']); return; } // Log extracted values error_log('Extracted Purchase Details: ' . print_r($purchaseDetails, true)); // Set Stripe API key Stripe::setApiKey('sk_test_51HiSUoGozbMWFnurBqY9URXX7pEVd0Rwnm9kyyyXuOr9pKNluCdpNp522HiGN65djoplcuJcCKjiXqtFBgZoM4f000XfvRgSgi'); try { // Create the PaymentIntent $paymentIntent = \Stripe\PaymentIntent::create([ 'amount' => intval($purchaseDetails['totalPrice'] * 100), // Amount in cents 'currency' => 'eur', 'payment_method_types' => ['card'], 'transfer_data' => [ 'destination' => $purchaseDetails['sellerAccountId'], ], ]); error_log('PaymentIntent Created: ' . json_encode($paymentIntent)); // Log PaymentIntent status if ($paymentIntent->status !== 'succeeded') { error_log('PaymentIntent Status: ' . $paymentIntent->status); error_log('PaymentIntent Full Response: ' . print_r($paymentIntent, true)); echo json_encode(['error' => 'Payment failed']); return; } // Proceed with database operations $db = getDB(); $db->beginTransaction(); error_log('Database Transaction Started'); // Insert purchase details $insertSql = "INSERT INTO media_purchases (msg_id_fk, buyer_uid_fk, fraction_count, purchase_price, total_price, platform_fee, seller_earnings, purchase_date) VALUES (?, ?, ?, ?, ?, ?, ?, NOW())"; $stmt = $db->prepare($insertSql); if (!$stmt->execute([$purchaseDetails['mediaId'], $purchaseDetails['userId'], $purchaseDetails['fractionsToPurchase'], $purchaseDetails['pricePerFraction'], $purchaseDetails['totalPrice'], $purchaseDetails['platformFee'], $purchaseDetails['sellerEarnings']])) { error_log('Failed to Insert Media Purchase: ' . json_encode($stmt->errorInfo())); throw new Exception('Failed to insert purchase details'); } error_log('Media Purchase Inserted Successfully'); // Commit transaction $db->commit(); error_log('Database Transaction Committed Successfully'); echo json_encode(['success' => true, 'message' => 'Purchase completed successfully']); } catch (\Stripe\Exception\ApiErrorException $e) { error_log('Stripe API Error: ' . $e->getMessage()); echo json_encode(['error' => 'Transaction failed']); } catch (Exception $e) { error_log('General Error: ' . $e->getMessage()); if (isset($db) && $db->inTransaction()) { $db->rollBack(); error_log('Database Transaction Rolled Back'); } echo json_encode(['error' => 'Transaction failed']); } } THE CONSOLE RETURNS THESE Payment details being sent to backend: {amount: 300, currency: 'eur', email: 'pellapost@outlook.com', seller_connected_account_id: 'acct_1Qevz92eCjM0J1d3'} user-data.ts:541 Calling createPaymentIntent API: https://project.com/api/api/createPaymentIntent user-data.ts:542 Request data: api_signature=bcbf2fd292fa27b76d509742cdc007e2&amount=300¤cy=eur&email=pellapost%40outlook.com&seller_connected_account_id=acct_1Qevz92eCjM0J1d3 user-data.ts:546 createPaymentIntent API response: {success: true, paymentIntentId: 'pi_3QgjzmGozbMWFnur0N7W0dLL', clientSecret: 'pi_3QgjzmGozbMWFnur0N7W0dLL_secret_j4J2sGD89jO3gjFgETtktYe4A', customerId: 'cus_RZtnd76eH7eIVl', amount: 300, …} sell-details.page.ts:610 Response from backend (createPaymentIntent): {success: true, paymentIntentId: 'pi_3QgjzmGozbMWFnur0N7W0dLL', clientSecret: 'pi_3QgjzmGozbMWFnur0N7W0dLL_secret_j4J2sGD89jO3gjFgETtktYe4A', customerId: 'cus_RZtnd76eH7eIVl', amount: 300, …} sell-details.page.ts:616 Client secret received: pi_3QgjzmGozbMWFnur0N7W0dLL_secret_j4J2sGD89jO3gjFgETtktYe4A sell-details.page.ts:617 Buyer customer ID: cus_RZtnd76eH7eIVl sell-details.page.ts:654 Starting purchaseMediaFractions... sell-details.page.ts:718 Payment successful: {id: 'pi_3QgjzmGozbMWFnur0N7W0dLL', object: 'payment_intent', amount: 300, amount_details: {…}, automatic_payment_methods: {…}, …} sell-details.page.ts:740 Purchase details: {userId: '1122', mediaId: '815', fractionsToPurchase: 3, pricePerFraction: '1.00', totalPrice: 3, …} sell-details.page.ts:745 Backend response for purchaseMediaFractions: {error: 'Payment failed'} sell-details.page.ts:758 Purchase failed: Payment failed and the error logs from php functions are these [13-Jan-2025 11:31:42 Europe/Athens] Received API Signature: bcbf2fd292fa27b76d509742cdc007e2, Amount: 300, Currency: eur, Email: pellapost@outlook.com, Seller Account ID: acct_1Qevz92eCjM0J1d3 [13-Jan-2025 11:31:43 Europe/Athens] Stripe Customer Created: {"id":"cus_RZtnd76eH7eIVl","object":"customer","address":null,"balance":0,"created":1736760702,"currency":null,"default_source":null,"delinquent":false,"description":"One-time customer for purchase","discount":null,"email":"pellapost@outlook.com","invoice_prefix":"2C322EAE","invoice_settings":{"custom_fields":null,"default_payment_method":null,"footer":null,"rendering_options":null},"livemode":false,"metadata":[],"name":null,"phone":null,"preferred_locales":[],"shipping":null,"tax_exempt":"none","test_clock":null} [13-Jan-2025 11:31:43 Europe/Athens] PaymentIntent Parameters: {"amount":300,"currency":"eur","customer":"cus_RZtnd76eH7eIVl","description":"Purchase","transfer_data":{"destination":"acct_1Qevz92eCjM0J1d3"},"application_fee_amount":30} [13-Jan-2025 11:31:43 Europe/Athens] PaymentIntent Created: {"id":"pi_3QgjzmGozbMWFnur0N7W0dLL","object":"payment_intent","amount":300,"amount_capturable":0,"amount_details":{"tip":[]},"amount_received":0,"application":null,"application_fee_amount":30,"automatic_payment_methods":{"allow_redirects":"always","enabled":true},"canceled_at":null,"cancellation_reason":null,"capture_method":"automatic","client_secret":"pi_3QgjzmGozbMWFnur0N7W0dLL_secret_j4J2sGD89jO3gjFgETtktYe4A","confirmation_method":"automatic","created":1736760702,"currency":"eur","customer":"cus_RZtnd76eH7eIVl","description":"Purchase","invoice":null,"last_payment_error":null,"latest_charge":null,"livemode":false,"metadata":[],"next_action":null,"on_behalf_of":null,"payment_method":null,"payment_method_configuration_details":{"id":"pmc_1PgT4IGozbMWFnurJXRBOp2V","parent":null},"payment_method_options":{"bancontact":{"preferred_language":"en"},"card":{"installments":null,"mandate_options":null,"network":null,"request_three_d_secure":"automatic"},"eps":[],"giropay":[],"ideal":[],"klarna":{"preferred_locale":null},"link":{"persistent_token":null}},"payment_method_types":["card","bancontact","eps","giropay","ideal","klarna","link"],"processing":null,"receipt_email":null,"review":null,"setup_future_usage":null,"shipping":null,"source":null,"statement_descriptor":null,"statement_descriptor_suffix":null,"status":"requires_payment_method","transfer_data":{"destination":"acct_1Qevz92eCjM0J1d3"},"transfer_group":null} [13-Jan-2025 11:32:14 Europe/Athens] Function Called: insertMediaPurchaseDetails [13-Jan-2025 11:32:14 Europe/Athens] Request Body: {"api_signature":"bcbf2fd292fa27b76d509742cdc007e2","purchaseDetails":{"userId":"1122","mediaId":"815","fractionsToPurchase":3,"pricePerFraction":"1.00","totalPrice":3,"platformFee":0.30000000000000004,"sellerEarnings":2.7,"sellerAccountId":"acct_1Qevz92eCjM0J1d3","buyerCustomerId":"cus_RZtnd76eH7eIVl"}} [13-Jan-2025 11:32:14 Europe/Athens] Extracted Purchase Details: Array ( [userId] => 1122 [mediaId] => 815 [fractionsToPurchase] => 3 [pricePerFraction] => 1.00 [totalPrice] => 3 [platformFee] => 0.3 [sellerEarnings] => 2.7 [sellerAccountId] => acct_1Qevz92eCjM0J1d3 [buyerCustomerId] => cus_RZtnd76eH7eIVl ) [13-Jan-2025 11:32:14 Europe/Athens] PaymentIntent Created: {"id":"pi_3Qgk0HGozbMWFnur0JDICtzQ","object":"payment_intent","amount":300,"amount_capturable":0,"amount_details":{"tip":[]},"amount_received":0,"application":null,"application_fee_amount":null,"automatic_payment_methods":null,"canceled_at":null,"cancellation_reason":null,"capture_method":"automatic","client_secret":"pi_3Qgk0HGozbMWFnur0JDICtzQ_secret_y4F4woUVuodAF2F4WANJl96Vm","confirmation_method":"automatic","created":1736760733,"currency":"eur","customer":null,"description":null,"invoice":null,"last_payment_error":null,"latest_charge":null,"livemode":false,"metadata":[],"next_action":null,"on_behalf_of":null,"payment_method":null,"payment_method_configuration_details":null,"payment_method_options":{"card":{"installments":null,"mandate_options":null,"network":null,"request_three_d_secure":"automatic"}},"payment_method_types":["card"],"processing":null,"receipt_email":null,"review":null,"setup_future_usage":null,"shipping":null,"source":null,"statement_descriptor":null,"statement_descriptor_suffix":null,"status":"requires_payment_method","transfer_data":{"destination":"acct_1Qevz92eCjM0J1d3"},"transfer_group":null} [13-Jan-2025 11:32:14 Europe/Athens] PaymentIntent Status: requires_payment_method [13-Jan-2025 11:32:14 Europe/Athens] PaymentIntent Full Response: Stripe\PaymentIntent Object ( [id] => pi_3Qgk0HGozbMWFnur0JDICtzQ [object] => payment_intent [amount] => 300 [amount_capturable] => 0 [amount_details] => Stripe\StripeObject Object ( [tip] => Array ( ) ) [amount_received] => 0 [application] => [application_fee_amount] => [automatic_payment_methods] => [canceled_at] => [cancellation_reason] => [capture_method] => automatic [client_secret] => pi_3Qgk0HGozbMWFnur0JDICtzQ_secret_y4F4woUVuodAF2F4WANJl96Vm [confirmation_method] => automatic [created] => 1736760733 [currency] => eur [customer] => [description] => [invoice] => [last_payment_error] => [latest_charge] => [livemode] => [metadata] => Stripe\StripeObject Object ( ) [next_action] => [on_behalf_of] => [payment_method] => [payment_method_configuration_details] => [payment_method_options] => Stripe\StripeObject Object ( [card] => Stripe\StripeObject Object ( [installments] => [mandate_options] => [network] => [request_three_d_secure] => automatic ) ) [payment_method_types] => Array ( [0] => card ) [processing] => [receipt_email] => [review] => [setup_future_usage] => [shipping] => [source] => [statement_descriptor] => [statement_descriptor_suffix] => [status] => requires_payment_method [transfer_data] => Stripe\StripeObject Object ( [destination] => acct_1Qevz92eCjM0J1d3 ) [transfer_group] => ) What am i doing wrong? Thanks in advance [1]: https://i.sstatic.net/DdkrgZS4.png Continue reading...