Closed amici-infotech closed 3 years ago
Thanks, I can reproduce this issue and am looking into it now.
Hey @lukeholder, I needed this for client so what I did is, turn off unique contrain for orderId,gatewayId,customerId
.
Then, I have extended payments intents class in my custom plugin and commented this line.
Unfortunately, I will not be able to generate proper refund with this as it generates 2 transactions in table. Any solution on this will be much appreciated :)
Any update on this one?
Hey @lukeholder Its been forever. Craft Commerce's native partial payment feature is useless for me without this as I always use Stripe payment gateway. Any updates on this would be much appreciated. Thanks :)
(Just making sure this is not a dead thread!!)
Hi @lukeholder, I can still recreate this problem. Do you have any update on when this might be fixed?
Craft Commerce 3.3.2 Stripe for Craft Commerce 2.3.2.1
Is there any suggested workaround since because of this Stripe Gateway currently cannot support partial payments?
@ivarsbariss-solspace I make it work with a custom plugin.
First of all, I have created a module and register a new Gateway. (that file just represent Actual Payment Gateway)
use craft\commerce\services\Gateways;
use craft\events\RegisterComponentTypesEvent;
use modules\mycustomplugin\gateways\PaymentIntents;
Event::on(Gateways::class, Gateways::EVENT_REGISTER_GATEWAY_TYPES, function(RegisterComponentTypesEvent $event) {
$event->types[] = PaymentIntents::class;
});
my PaymentIntents.php file looks like this:
<?php
namespace modules\mycustommodule\gateways;
use craft\commerce\stripe\gateways\PaymentIntents as PaymentIntentsMain;
use modules\mycustommodule\MyCustomModule;
use Craft;
use craft\commerce\base\RequestResponseInterface;
use craft\commerce\models\payments\BasePaymentForm;
use craft\commerce\models\Transaction;
use craft\commerce\stripe\models\PaymentIntent as PaymentIntentModel;
use craft\commerce\stripe\Plugin as StripePlugin;
use craft\helpers\UrlHelper;
use Stripe\PaymentIntent;
// Extending Stripe's PaymentIntent Gateway so we dont have to copy all the functions but only one which we will change code into.
class PaymentIntents extends PaymentIntentsMain
{
/**
* @inheritdoc
*/
public static function displayName(): string
{
return Craft::t('mycustommodule', 'Custom Module - Stripe Payment Intents');
}
/**
* @inheritdoc
*/
protected function authorizeOrPurchase(Transaction $transaction, BasePaymentForm $form, bool $capture = true): RequestResponseInterface
{
// Custom Deposit code added by Mufi (Changing payment amount from full to deposit only, this was the goal to create this file.)
$deposit = MyCustomModule::$instance->DepositCalculations->calc($transaction->getOrder());
if($deposit > 0)
{
$transaction->amount = $deposit;
$transaction->paymentAmount = $deposit;
}
$this->configureStripeClient();
/** @var PaymentForm $form */
$requestData = $this->buildRequestData($transaction);
$paymentMethodId = $form->paymentMethodId;
$customer = null;
$paymentIntent = null;
$stripePlugin = StripePlugin::getInstance();
if ($form->customer) {
$requestData['customer'] = $form->customer;
$customer = $stripePlugin->getCustomers()->getCustomerByReference($form->customer);
} else if ($user = $transaction->getOrder()->getUser()) {
$customer = $stripePlugin->getCustomers()->getCustomer($this->id, $user);
$requestData['customer'] = $customer->reference;
}
$requestData['payment_method'] = $paymentMethodId;
try {
// If this is a customer that's logged in, attempt to continue the timeline
if ($customer) {
$paymentIntentService = $stripePlugin->getPaymentIntents();
// Commented by Mufi
// $paymentIntent = $paymentIntentService->getPaymentIntent($this->id, $transaction->orderId, $customer->id);
}
// If a payment intent exists, update that.
if ($paymentIntent) {
$stripePaymentIntent = PaymentIntent::update($paymentIntent->reference, $requestData, ['idempotency_key' => $transaction->hash]);
} else {
$requestData['capture_method'] = $capture ? 'automatic' : 'manual';
$requestData['confirmation_method'] = 'manual';
$requestData['confirm'] = false;
$stripePaymentIntent = PaymentIntent::create($requestData, ['idempotency_key' => $transaction->hash]);
if ($customer) {
$paymentIntent = new PaymentIntentModel([
'orderId' => $transaction->orderId,
'customerId' => $customer->id,
'gatewayId' => $this->id,
'reference' => $stripePaymentIntent->id,
]);
}
}
if ($paymentIntent) {
// Save data before confirming.
$paymentIntent->intentData = $stripePaymentIntent->jsonSerialize();
$paymentIntentService->savePaymentIntent($paymentIntent);
}
$this->_confirmPaymentIntent($stripePaymentIntent, $transaction);
return $this->createPaymentResponseFromApiResource($stripePaymentIntent);
} catch (\Exception $exception) {
return $this->createPaymentResponseFromError($exception);
}
}
/**
* Confirm a payment intent and set the return URL.
*
* @param PaymentIntent $stripePaymentIntent
*/
private function _confirmPaymentIntent(PaymentIntent $stripePaymentIntent, Transaction $transaction)
{
$this->configureStripeClient();
$stripePaymentIntent->confirm([
'return_url' => UrlHelper::actionUrl('commerce/payments/complete-payment', ['commerceTransactionId' => $transaction->id, 'commerceTransactionHash' => $transaction->hash])
]);
}
}
The idea was to extend actual payment gateway and modify the authorizeOrPurchase
function to add Deposit and comment out the line where paymentIntents goes to check for old row into database because that part was throwing error.
Hope this helps.
@amici-infotech @nickcobley @ivarsbariss-solspace Fix is in PR #181
You are welcome to test beforehand, but will be in the next release.
@lukeholder Thank you! Do you have an approximate timeline for the next release?
@lukeholder I tried to test the PR #181 but after installing the fix branch's version and running the migrations I got this:
Database Exception: SQLSTATE[HY000]: General error: 1553 Cannot drop index 'idx_kakionsisopbqabdirzzlztqrqhtbiwphmac': needed in a foreign key constraint
The SQL being executed was: DROP INDEX idx_kakionsisopbqabdirzzlztqrqhtbiwphmac ON stripe_paymentintents
Migration: craft\commerce\stripe\migrations\m210903_040320_payment_intent_unique_on_transaction
Output:
> add column transactionHash string AFTER orderId to table {{%stripe_paymentintents}} ... done (time: 0.082s)
> drop index idx_kakionsisopbqabdirzzlztqrqhtbiwphmac on {{%stripe_paymentintents}} ...Exception: SQLSTATE[HY000]: General error: 1553 Cannot drop index 'idx_kakionsisopbqabdirzzlztqrqhtbiwphmac': needed in a foreign key constraint
The SQL being executed was: DROP INDEX idx_kakionsisopbqabdirzzlztqrqhtbiwphmac ON stripe_paymentintents (/Users/ivarsbariss/Web/craft-3/vendor/yiisoft/yii2/db/Schema.php:678)
#0 /Users/ivarsbariss/Web/craft-3/vendor/yiisoft/yii2/db/Command.php(1304): yii\db\Schema->convertException(Object(PDOException), 'DROP INDEX idx...')
#1 /Users/ivarsbariss/Web/craft-3/vendor/yiisoft/yii2/db/Command.php(1099): yii\db\Command->internalExecute('DROP INDEX idx...')
#2 /Users/ivarsbariss/Web/craft-3/vendor/yiisoft/yii2/db/Migration.php(507): yii\db\Command->execute()
#3 /Users/ivarsbariss/Web/craft-3/vendor/craftcms/cms/src/helpers/MigrationHelper.php(754): yii\db\Migration->dropIndex('idx_kakionsisop...', '{{%stripe_payme...')
#4 /Users/ivarsbariss/Web/craft-3/vendor/craftcms/cms/src/helpers/MigrationHelper.php(580): craft\helpers\MigrationHelper::_dropIndex('{{%stripe_payme...', 'idx_kakionsisop...', Object(craft\commerce\stripe\migrations\m210903_040320_payment_intent_unique_on_transaction))
#5 /Users/ivarsbariss/Web/craft-3/plugins/commerce-stripe-feature-fix-147/src/migrations/m210903_040320_payment_intent_unique_on_transaction.php(26): craft\helpers\MigrationHelper::dropAllIndexesOnTable('{{%stripe_payme...', Object(craft\commerce\stripe\migrations\m210903_040320_payment_intent_unique_on_transaction))
#6 /Users/ivarsbariss/Web/craft-3/vendor/craftcms/cms/src/db/Migration.php(52): craft\commerce\stripe\migrations\m210903_040320_payment_intent_unique_on_transaction->safeUp()
#7 /Users/ivarsbariss/Web/craft-3/vendor/craftcms/cms/src/db/MigrationManager.php(232): craft\db\Migration->up(true)
#8 /Users/ivarsbariss/Web/craft-3/vendor/craftcms/cms/src/db/MigrationManager.php(148): craft\db\MigrationManager->migrateUp(Object(craft\commerce\stripe\migrations\m210903_040320_payment_intent_unique_on_transaction))
#9 /Users/ivarsbariss/Web/craft-3/vendor/craftcms/cms/src/services/Updates.php(257): craft\db\MigrationManager->up()
#10 /Users/ivarsbariss/Web/craft-3/vendor/craftcms/cms/src/controllers/BaseUpdaterController.php(509): craft\services\Updates->runMigrations(Array)
#11 /Users/ivarsbariss/Web/craft-3/vendor/craftcms/cms/src/controllers/UpdaterController.php(203): craft\controllers\BaseUpdaterController->runMigrations(Array, 'restore-db')
#12 [internal function]: craft\controllers\UpdaterController->actionMigrate()
#13 /Users/ivarsbariss/Web/craft-3/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array(Array, Array)
#14 /Users/ivarsbariss/Web/craft-3/vendor/yiisoft/yii2/base/Controller.php(181): yii\base\InlineAction->runWithParams(Array)
#15 /Users/ivarsbariss/Web/craft-3/vendor/craftcms/cms/src/web/Controller.php(190): yii\base\Controller->runAction('migrate', Array)
#16 /Users/ivarsbariss/Web/craft-3/vendor/yiisoft/yii2/base/Module.php(534): craft\web\Controller->runAction('migrate', Array)
#17 /Users/ivarsbariss/Web/craft-3/vendor/craftcms/cms/src/web/Application.php(274): yii\base\Module->runAction('updater/migrate', Array)
#18 /Users/ivarsbariss/Web/craft-3/vendor/craftcms/cms/src/web/Application.php(665): craft\web\Application->runAction('updater/migrate')
#19 /Users/ivarsbariss/Web/craft-3/vendor/craftcms/cms/src/web/Application.php(232): craft\web\Application->_processUpdateLogic(Object(craft\web\Request))
#20 /Users/ivarsbariss/Web/craft-3/vendor/yiisoft/yii2/base/Application.php(392): craft\web\Application->handleRequest(Object(craft\web\Request))
#21 /Users/ivarsbariss/Web/craft-3/public/index.php(25): yii\base\Application->run()
#22 {main}
Hi @lukeholder, any update on this fix's bug and the timeline for the release?
@lukeholder if this helps I was able to run the migration by adding this line to your migration before dropping the unique indexes:
MigrationHelper::dropAllForeignKeysOnTable('{{%stripe_paymentintents}}', $this);
I bet you need to put back correctly the foreign keys at the end of the migration just like you did with the Unique indexes.
@lukeholder Can you please advise if Craft plans to address this bug? We selected Craft Commerce for a major e-commerce project ~ 6 months ago with Partial Payments being a major component of that selection, and are awaiting a fix on this before we can launch the new site.
@apitel @ivarsbariss-solspace Sorry about this. I was monitoring the PR #181 for feedback and missed this issue feedback. I have fixed the FK error.
Please add feedback to the PR and I will merge and update Stripe today.
I am trying to build a workaround so clients can pay deposits. I have used this branch of craft commece. (they said, they will implement it in commerce 4) https://github.com/craftcms/commerce/tree/feature/3.x-partial-payments
When I use this feature branch with Stripe intents payment, I can pay depoit, but I cant pay final amount. Got this error from stripe:
I think this is happening because Stripe mark this order as finished in Stripe Dashboard. Is there any way I can tell Stripe that this payment is partial?