Как исправить оплату Stripe для подключенных учетных записейPhp

Кемеровские программисты php общаются здесь
Ответить Пред. темаСлед. тема
Anonymous
 Как исправить оплату Stripe для подключенных учетных записей

Сообщение Anonymous »

Здравствуйте, у меня возникла проблема с оплатой Stripe от покупателя. Я хочу, чтобы покупатель мог покупать цифровой контент на моей платформе, и мы удерживаем с покупателя комиссию. Я хочу, чтобы покупка совершалась на бэкэнде. Я использую Stripe в другом случае, и он реализован нормально.

[*]Учетная запись продавца создана нормально, и продавец проверен на платформе Stripe.[*]Платеж от покупателя сначала прошел успешно, а затем не удалось. Как я показываю вам на своей панели тестирования полосок на фотографии, которой я поделился, я думаю, что намерение платежа возвращается неполным.
[![введите описание изображения здесь][1]][1]
< /ol>
КОД
ВНЕШНЯЯ ЧАСТЬ

Код: Выделить всё

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();
}
}
Пользовательские данные служб

Код: Выделить всё

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'),
});
}
И ФУНКЦИИ PHP

Код: Выделить всё

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  $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']);
}
}
КОНСОЛЬ ВЕРНУЕТ ЭТО

Код: Выделить всё

Payment details being sent to backend: {amount: 300, currency: 'eur', email: '[email protected]', 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&currency=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
и журналы ошибок функций PHP — это

Код: Выделить всё

[13-Jan-2025 11:31:42 Europe/Athens] Received API Signature: bcbf2fd292fa27b76d509742cdc007e2, Amount: 300, Currency: eur, Email: [email protected], 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":"[email protected]","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] =>
)
Что я делаю не так? Заранее спасибо
[1]: https://i.sstatic.net/DdkrgZS4.png

Подробнее здесь: https://stackoverflow.com/questions/793 ... d-accounts
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «Php»