Debug payment SEPA from backoffice with Stripe

This commit is contained in:
Laurent Destailleur 2023-01-18 14:36:46 +01:00
parent 330f2307bd
commit a384bdecdc
2 changed files with 77 additions and 143 deletions

View File

@ -807,13 +807,13 @@ if ($object->id > 0) {
print '<td class="center">'.$langs->trans("User").'</td>'; print '<td class="center">'.$langs->trans("User").'</td>';
print '<td class="center">'.$langs->trans("Amount").'</td>'; print '<td class="center">'.$langs->trans("Amount").'</td>';
print '<td class="center">'.$langs->trans("DateProcess").'</td>'; print '<td class="center">'.$langs->trans("DateProcess").'</td>';
print '<td>&nbsp;</td>';
if ($type == 'bank-transfer') { if ($type == 'bank-transfer') {
print '<td class="center">'.$langs->trans("BankTransferReceipt").'</td>'; print '<td class="center">'.$langs->trans("BankTransferReceipt").'</td>';
} else { } else {
print '<td class="center">'.$langs->trans("WithdrawalReceipt").'</td>'; print '<td class="center">'.$langs->trans("WithdrawalReceipt").'</td>';
} }
print '<td>&nbsp;</td>'; print '<td>&nbsp;</td>';
print '<td>&nbsp;</td>';
print '</tr>'; print '</tr>';
$sql = "SELECT pfd.rowid, pfd.traite, pfd.date_demande as date_demande,"; $sql = "SELECT pfd.rowid, pfd.traite, pfd.date_demande as date_demande,";
@ -864,9 +864,10 @@ if ($object->id > 0) {
// Amount // Amount
print '<td class="center"><span class="amount">'.price($obj->amount).'</span></td>'; print '<td class="center"><span class="amount">'.price($obj->amount).'</span></td>';
// Ref of SEPA request // Date process
print '<td class="center"><span class="opacitymedium">'.$langs->trans("OrderWaiting").'</span></td>'; print '<td class="center"><span class="opacitymedium">'.$langs->trans("OrderWaiting").'</span></td>';
// Link to make payment now
print '<td>'; print '<td>';
if (!empty($conf->global->STRIPE_SEPA_DIRECT_DEBIT)) { if (!empty($conf->global->STRIPE_SEPA_DIRECT_DEBIT)) {
$langs->load("stripe"); $langs->load("stripe");
@ -874,8 +875,10 @@ if ($object->id > 0) {
} }
print '</td>'; print '</td>';
//
print '<td align="center">-</td>'; print '<td align="center">-</td>';
// Actions
print '<td class="right">'; print '<td class="right">';
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=delete&token='.newToken().'&did='.$obj->rowid.'&type='.$type.'">'; print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=delete&token='.newToken().'&did='.$obj->rowid.'&type='.$type.'">';
print img_delete(); print img_delete();
@ -929,14 +932,21 @@ if ($object->id > 0) {
print '<tr class="oddeven">'; print '<tr class="oddeven">';
// Date
print '<td class="left">'.dol_print_date($db->jdate($obj->date_demande), 'day')."</td>\n"; print '<td class="left">'.dol_print_date($db->jdate($obj->date_demande), 'day')."</td>\n";
// User
print '<td align="center">'; print '<td align="center">';
print $tmpuser->getNomUrl(1, '', 0, 0, 0, 0, 'login'); print $tmpuser->getNomUrl(1, '', 0, 0, 0, 0, 'login');
print '</td>'; print '</td>';
// Amount
print '<td class="center">'.price($obj->amount).'</td>'; print '<td class="center">'.price($obj->amount).'</td>';
// Date process
print '<td class="center">'.dol_print_date($db->jdate($obj->date_traite), 'day')."</td>\n";
// Link to payment request done
print '<td class="center">'; print '<td class="center">';
if ($obj->fk_prelevement_bons > 0) { if ($obj->fk_prelevement_bons > 0) {
$withdrawreceipt = new BonPrelevement($db); $withdrawreceipt = new BonPrelevement($db);
@ -946,10 +956,10 @@ if ($object->id > 0) {
} }
print "</td>\n"; print "</td>\n";
//
print '<td>&nbsp;</td>'; print '<td>&nbsp;</td>';
print '<td class="center">'.dol_print_date($db->jdate($obj->date_traite), 'day')."</td>\n"; // Actions
print '<td>&nbsp;</td>'; print '<td>&nbsp;</td>';
print "</tr>\n"; print "</tr>\n";

View File

@ -865,7 +865,7 @@ abstract class CommonInvoice extends CommonObject
/** /**
* Create a withdrawal request for a direct debit order or a credit transfer order. * Create a withdrawal request at Stripe for a direct debit order or a credit transfer order.
* Use the remain to pay excluding all existing open direct debit requests. * Use the remain to pay excluding all existing open direct debit requests.
* *
* @param User $fuser User asking the direct debit transfer * @param User $fuser User asking the direct debit transfer
@ -900,6 +900,7 @@ abstract class CommonInvoice extends CommonObject
$sql = "SELECT rowid, date_demande, amount, fk_facture, fk_facture_fourn"; $sql = "SELECT rowid, date_demande, amount, fk_facture, fk_facture_fourn";
$sql .= " FROM ".$this->db->prefix()."prelevement_demande"; $sql .= " FROM ".$this->db->prefix()."prelevement_demande";
$sql .= " AND fk_facture = ".((int) $this->fk_facture); // Add a protection to not pay another invoice than current one
$sql .= " WHERE rowid = ".((int) $did); $sql .= " WHERE rowid = ".((int) $did);
dol_syslog(get_class($this)."::makeStripeSepaRequest 1", LOG_DEBUG); dol_syslog(get_class($this)."::makeStripeSepaRequest 1", LOG_DEBUG);
@ -1035,9 +1036,9 @@ abstract class CommonInvoice extends CommonObject
} }
dol_syslog("makeStripeSepaRequest get stripe account", LOG_DEBUG); dol_syslog("makeStripeSepaRequest get stripe connet account", LOG_DEBUG);
$stripeacc = $stripe->getStripeAccount($service, $this->socid); // Get Stripe OAuth connect account if it exists (no network access here) $stripeacc = $stripe->getStripeAccount($service, $this->socid); // Get Stripe OAuth connect account if it exists (no network access here)
dol_syslog("makeStripeSepaRequest get stripe account return " . json_encode($stripeacc), LOG_DEBUG); dol_syslog("makeStripeSepaRequest get stripe connect account return " . json_encode($stripeacc), LOG_DEBUG);
if ($foundalternativestripeaccount) { if ($foundalternativestripeaccount) {
if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
@ -1072,62 +1073,26 @@ abstract class CommonInvoice extends CommonObject
} }
if (!$error) { // Payment was not canceled if (!$error) { // Payment was not canceled
//erics card or sepa ?
$sepaMode = false; $sepaMode = false;
$stripecard = null;
if ($companypaymentmode->type == 'ban') { if ($companypaymentmode->type == 'ban') {
$sepaMode = true; $sepaMode = true;
$stripecard = $stripe->sepaStripe($customer, $companypaymentmode, $stripeacc, $servicestatus, 0); $stripecard = $stripe->sepaStripe($customer, $companypaymentmode, $stripeacc, $servicestatus, 0);
} else {
$stripecard = $stripe->cardStripe($customer, $companypaymentmode, $stripeacc, $servicestatus, 0);
} }
if ($stripecard) { // Can be card_... (old mode) or pm_... (new mode) if ($stripecard) { // Can be src_... (for sepa) - Other card_... (old mode) or pm_... (new mode) should not happen here.
$FULLTAG = 'INV=' . $this->id . '-CUS=' . $thirdparty->id; $FULLTAG = 'INV=' . $this->id . '-CUS=' . $thirdparty->id;
$description = 'Stripe payment from doTakePaymentStripeForThirdparty: ' . $FULLTAG . ' ref=' . $this->ref; $description = 'Stripe payment from makeStripeSepaRequest: ' . $FULLTAG . ' ref=' . $this->ref;
$stripefailurecode = ''; $stripefailurecode = '';
$stripefailuremessage = ''; $stripefailuremessage = '';
$stripefailuredeclinecode = ''; $stripefailuredeclinecode = '';
if (preg_match('/^card_/', $stripecard->id)) { // Using old method // Using new SCA method
dol_syslog("* Create charge on card " . $stripecard->id . ", amountstripe=" . $amountstripe . ", FULLTAG=" . $FULLTAG, LOG_DEBUG);
$ipaddress = getUserRemoteIP();
$charge = null; // Force reset of $charge, so, if already set from a previous fetch, it will be empty even if there is an exception at next step
try {
$charge = \Stripe\Charge::create([
'amount' => price2num($amountstripe, 'MU'),
'currency' => $currency,
'capture' => true, // Charge immediatly
'description' => $description,
'metadata' => ["FULLTAG" => $FULLTAG, 'Recipient' => $mysoc->name, 'dol_version' => DOL_VERSION, 'dol_entity' => $conf->entity, 'ipaddress' => $ipaddress],
'customer' => $customer->id,
//'customer' => 'bidon_to_force_error', // To use to force a stripe error
'source' => $stripecard,
'statement_descriptor' => dol_trunc('INV=' . $this->id, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description)
]);
} catch (\Stripe\Error\Card $e) {
// Since it's a decline, Stripe_CardError will be caught
$body = $e->getJsonBody();
$err = $body['error'];
$stripefailurecode = $err['code'];
$stripefailuremessage = $err['message'];
$stripefailuredeclinecode = $err['decline_code'];
} catch (Exception $e) {
$stripefailurecode = 'UnknownChargeError';
$stripefailuremessage = $e->getMessage();
}
} else { // Using new SCA method
if ($sepaMode) {
dol_syslog("* Create payment on SEPA " . $stripecard->id . ", amounttopay=" . $amounttopay . ", amountstripe=" . $amountstripe . ", FULLTAG=" . $FULLTAG, LOG_DEBUG); dol_syslog("* Create payment on SEPA " . $stripecard->id . ", amounttopay=" . $amounttopay . ", amountstripe=" . $amountstripe . ", FULLTAG=" . $FULLTAG, LOG_DEBUG);
} else {
dol_syslog("* Create payment on card " . $stripecard->id . ", amounttopay=" . $amounttopay . ", amountstripe=" . $amountstripe . ", FULLTAG=" . $FULLTAG, LOG_DEBUG);
}
// Create payment intent and charge payment (confirmnow = true) // Create payment intent and charge payment (confirmnow = true)
$paymentintent = $stripe->getPaymentIntent($amounttopay, $currency, $FULLTAG, $description, $invoice, $customer->id, $stripeacc, $servicestatus, 0, 'automatic', true, $stripecard->id, 1); $paymentintent = $stripe->getPaymentIntent($amounttopay, $currency, $FULLTAG, $description, $this, $customer->id, $stripeacc, $servicestatus, 0, 'automatic', true, $stripecard->id, 1);
$charge = new stdClass(); $charge = new stdClass();
//erics add processing sepa is like success ? //erics add processing sepa is like success ?
@ -1162,11 +1127,10 @@ abstract class CommonInvoice extends CommonObject
//var_dump("stripefailurecode=".$stripefailurecode." stripefailuremessage=".$stripefailuremessage." stripefailuredeclinecode=".$stripefailuredeclinecode); //var_dump("stripefailurecode=".$stripefailurecode." stripefailuremessage=".$stripefailuremessage." stripefailuredeclinecode=".$stripefailuredeclinecode);
//exit; //exit;
}
// Return $charge = array('id'=>'ch_XXXX', 'status'=>'succeeded|pending|failed', 'failure_code'=>, 'failure_message'=>...) // Return $charge = array('id'=>'ch_XXXX', 'status'=>'succeeded|pending|failed', 'failure_code'=>, 'failure_message'=>...)
if (empty($charge) || $charge->status == 'failed') { if (empty($charge) || $charge->status == 'failed') {
dol_syslog('Failed to charge card or payment mode ' . $stripecard->id . ' stripefailurecode=' . $stripefailurecode . ' stripefailuremessage=' . $stripefailuremessage . ' stripefailuredeclinecode=' . $stripefailuredeclinecode, LOG_WARNING); dol_syslog('Failed to charge payment mode ' . $stripecard->id . ' stripefailurecode=' . $stripefailurecode . ' stripefailuremessage=' . $stripefailuremessage . ' stripefailuredeclinecode=' . $stripefailuredeclinecode, LOG_WARNING);
// Save a stripe payment was in error // Save a stripe payment was in error
$this->stripechargeerror++; $this->stripechargeerror++;
@ -1202,20 +1166,20 @@ abstract class CommonInvoice extends CommonObject
$errmsg .= ($stripefailuredeclinecode ? ' - ' . $stripefailuredeclinecode : ''); $errmsg .= ($stripefailuredeclinecode ? ' - ' . $stripefailuredeclinecode : '');
} }
$description = 'Stripe payment ERROR from doTakePaymentStripeForThirdparty: ' . $FULLTAG; $description = 'Stripe payment ERROR from makeStripeSepaRequest: ' . $FULLTAG;
$postactionmessages[] = $errmsg . ' (' . $stripearrayofkeys['publishable_key'] . ')'; $postactionmessages[] = $errmsg . ' (' . $stripearrayofkeys['publishable_key'] . ')';
$this->errors[] = $errmsg; $this->errors[] = $errmsg;
} else { } else {
dol_syslog('Successfuly charge card ' . $stripecard->id); dol_syslog('Successfuly charge direct debit ' . $stripecard->id);
$postactionmessages[] = 'Success to charge card (' . $charge->id . ' with ' . $stripearrayofkeys['publishable_key'] . ')'; $postactionmessages[] = 'Success to charge direct debit (' . $charge->id . ' with ' . $stripearrayofkeys['publishable_key'] . ')';
// Save a stripe payment was done in realy life so later we will be able to force a commit on recorded payments // Save a stripe payment was done in realy life so later we will be able to force a commit on recorded payments
// even if in batch mode (method doTakePaymentStripe), we will always make all action in one transaction with a forced commit. // even if in batch mode (method doTakePaymentStripe), we will always make all action in one transaction with a forced commit.
$this->stripechargedone++; $this->stripechargedone++;
// Default description used for label of event. Will be overwrite by another value later. // Default description used for label of event. Will be overwrite by another value later.
$description = 'Stripe payment OK (' . $charge->id . ') from doTakePaymentStripeForThirdparty: ' . $FULLTAG; $description = 'Stripe payment OK (' . $charge->id . ') from makeStripeSepaRequest: ' . $FULLTAG;
$db = $this->db; $db = $this->db;
@ -1300,13 +1264,8 @@ abstract class CommonInvoice extends CommonObject
if (!$errorforinvoice && isModEnabled('banque')) { if (!$errorforinvoice && isModEnabled('banque')) {
dol_syslog('* Add payment to bank'); dol_syslog('* Add payment to bank');
// The bank used is the one defined into Stripe setup
$bankaccountid = 0; $bankaccountid = 0;
if ($paymentmethod == 'paybox') {
$bankaccountid = $conf->global->PAYBOX_BANK_ACCOUNT_FOR_PAYMENTS;
}
if ($paymentmethod == 'paypal') {
$bankaccountid = $conf->global->PAYPAL_BANK_ACCOUNT_FOR_PAYMENTS;
}
if ($paymentmethod == 'stripe') { if ($paymentmethod == 'stripe') {
$bankaccountid = $conf->global->STRIPE_BANK_ACCOUNT_FOR_PAYMENTS; $bankaccountid = $conf->global->STRIPE_BANK_ACCOUNT_FOR_PAYMENTS;
} }
@ -1323,7 +1282,7 @@ abstract class CommonInvoice extends CommonObject
$error++; $error++;
$errorforinvoice++; $errorforinvoice++;
} else { } else {
$postactionmessages[] = 'Bank transaction of payment created (by doTakePaymentStripeForThirdparty)'; $postactionmessages[] = 'Bank transaction of payment created (by makeStripeSepaRequest)';
} }
} else { } else {
$postactionmessages[] = 'Setup of bank account to use in module ' . $paymentmethod . ' was not set. No way to record the payment.'; $postactionmessages[] = 'Setup of bank account to use in module ' . $paymentmethod . ' was not set. No way to record the payment.';
@ -1334,55 +1293,24 @@ abstract class CommonInvoice extends CommonObject
} }
if ($ispostactionok < 1) { if ($ispostactionok < 1) {
$description = 'Stripe payment OK (' . $charge->id . ' - ' . $amounttopay . ' ' . $conf->currency . ') but post action KO from doTakePaymentStripeForThirdparty: ' . $FULLTAG; $description = 'Stripe payment OK (' . $charge->id . ' - ' . $amounttopay . ' ' . $conf->currency . ') but post action KO from makeStripeSepaRequest: ' . $FULLTAG;
} else { } else {
$description = 'Stripe payment+post action OK (' . $charge->id . ' - ' . $amounttopay . ' ' . $conf->currency . ') from doTakePaymentStripeForThirdparty: ' . $FULLTAG; $description = 'Stripe payment+post action OK (' . $charge->id . ' - ' . $amounttopay . ' ' . $conf->currency . ') from makeStripeSepaRequest: ' . $FULLTAG;
} }
} }
$object = $invoice; $object = $invoice;
// Send emails $actioncode = '';
$labeltouse = 'InvoicePaymentSuccess';
$sendemailtocustomer = 1;
if (empty($charge) || $charge->status == 'failed') {
$labeltouse = 'InvoicePaymentFailure';
if ($noemailtocustomeriferror) {
$sendemailtocustomer = 0;
} // $noemailtocustomeriferror is set when error already reported on myaccount screen
}
// Track an event
if (empty($charge) || $charge->status == 'failed') {
$actioncode = 'PAYMENT_STRIPE_KO';
$extraparams = $stripefailurecode;
$extraparams .= (($extraparams && $stripefailuremessage) ? ' - ' : '') . $stripefailuremessage;
$extraparams .= (($extraparams && $stripefailuredeclinecode) ? ' - ' : '') . $stripefailuredeclinecode;
} else {
$actioncode = 'PAYMENT_STRIPE_OK';
$extraparams = ''; $extraparams = '';
}
} else { } else {
$error++; $error++;
$errorforinvoice++; $errorforinvoice++;
dol_syslog("No card or payment method found for this stripe customer " . $customer->id, LOG_WARNING); dol_syslog("No direct debit payment method found for this stripe customer " . $customer->id, LOG_WARNING);
$this->errors[] = 'Failed to get card | payment method for stripe customer = ' . $customer->id; $this->errors[] = 'Failed to get direct debit payment method for stripe customer = ' . $customer->id;
$labeltouse = 'InvoicePaymentFailure';
$sendemailtocustomer = 1;
if ($noemailtocustomeriferror) {
$sendemailtocustomer = 0;
} // $noemailtocustomeriferror is set when error already reported on myaccount screen
$description = 'Failed to find or use the payment mode - no credit card defined for the customer account';
$stripefailurecode = 'BADPAYMENTMODE';
$stripefailuremessage = 'Failed to find or use the payment mode - no credit card defined for the customer account';
$postactionmessages[] = $description . ' (' . $stripearrayofkeys['publishable_key'] . ')';
$object = $invoice; $object = $invoice;
$actioncode = 'PAYMENT_STRIPE_KO'; $actioncode = '';
$extraparams = ''; $extraparams = '';
} }
} else { } else {
@ -1427,6 +1355,7 @@ abstract class CommonInvoice extends CommonObject
$extraparams = ''; $extraparams = '';
} }
/*
// Send email + create action after // Send email + create action after
if ($sendemailtocustomer && $labeltouse) { if ($sendemailtocustomer && $labeltouse) {
dol_syslog("* Send email with result of payment - " . $labeltouse); dol_syslog("* Send email with result of payment - " . $labeltouse);
@ -1537,6 +1466,7 @@ abstract class CommonInvoice extends CommonObject
} }
} }
} }
*/
if ($description) { if ($description) {
dol_syslog("* Record event for payment result - " . $description); dol_syslog("* Record event for payment result - " . $description);
@ -1588,18 +1518,12 @@ abstract class CommonInvoice extends CommonObject
$this->errors[] = "Remain to pay is null for the invoice " . $this->id . " " . $this->ref . ". Why is the invoice not classified 'Paid' ?"; $this->errors[] = "Remain to pay is null for the invoice " . $this->id . " " . $this->ref . ". Why is the invoice not classified 'Paid' ?";
} }
$sql = "INSERT INTO ".MAIN_DB_PREFIX."prelevement_demande("; // TODO Create a prelevement_bon ?
$sql .= "fk_facture, "; // For the moment no
$sql .= " amount, date_demande, fk_user_demande, ext_payment_id, ext_payment_site, sourcetype, entity)";
$sql .= " VALUES (".$this->id; // We must update the direct debit payment request as "done"
$sql .= ",".((float) price2num($amount)); $sql = "UPDATE".MAIN_DB_PREFIX."prelevement_demande SET traite = 1, date_traite = '".$this->db->idate(dol_now())."'";
$sql .= ",'".$this->db->idate($now)."'"; $sql .= "WHERE rowid = ".((int) $did);
$sql .= ",".((int) $fuser->id);
$sql .= ",'".$this->db->escape($stripe_id)."'";
$sql .= ",'".$this->db->escape($stripe_uri)."'";
$sql .= ",'".$this->db->escape($sourcetype)."'";
$sql .= ",".$conf->entity;
$sql .= ")";
dol_syslog(get_class($this)."::makeStripeSepaRequest", LOG_DEBUG); dol_syslog(get_class($this)."::makeStripeSepaRequest", LOG_DEBUG);
$resql = $this->db->query($sql); $resql = $this->db->query($sql);