Skip to content

Instantly share code, notes, and snippets.

@Tam
Last active September 23, 2022 04:07
Show Gist options
  • Save Tam/31aaa14b7ff274940efe40f701774c54 to your computer and use it in GitHub Desktop.
Save Tam/31aaa14b7ff274940efe40f701774c54 to your computer and use it in GitHub Desktop.
Add subscription pause support to Commerce Stripe
<?php
namespace modules;
use Craft;
use craft\commerce\elements\Subscription;
use DateTime;
use Twig\Error\LoaderError;
use Twig\Error\SyntaxError;
use yii\base\Module;
/**
* Class PauseSubscription
*
* @author Ether Creative
* @package modules
*/
class PauseSubscription extends Module
{
public function init ()
{
parent::init();
Craft::$app->getView()->hook(
'cp.commerce.subscriptions.edit.content',
[$this, 'hookCpCommerceSubscriptionsContentEdit']
);
}
/**
* @param array $context
*
* @return string
* @throws LoaderError
* @throws SyntaxError
*/
public function hookCpCommerceSubscriptionsContentEdit (array &$context)
{
$view = Craft::$app->getView();
$css = <<<CSS
.b-badge {
padding: 2px 6px;
font-size: 11px;
text-transform: uppercase;
line-height: 16px;
border-radius: 8px;
background-color: #596673;
color: #fff;
}
CSS;
$view->registerCss($css);
/** @var Subscription $subscription */
$subscription = $context['subscription'];
if ($subscription->isCanceled || $subscription->isExpired)
return '';
$paused = $subscription->getSubscriptionData()['pause_collection'];
$context['isPaused'] = !empty($paused);
$context['pausedUntil'] = null;
if ($context['isPaused'] && !empty($paused['resumes_at']))
$context['pausedUntil'] = (new DateTime())->setTimestamp($paused['resumes_at']);
$markup = <<<TWIG
<div class="pane">
<h2>Pause Subscription <span class="b-badge">Custom</span></h2>
<form method="POST">
<input type="hidden" name="action" value="pause-subscription/subscriptions/{{ isPaused ? 'resume' : 'pause' }}">
<input type="hidden" name="subscriptionUid" value="{{ subscription.uid|hash }}">
{{ redirectInput(continueEditingUrl) }}
{{ csrfInput() }}
<div class="field">
<div class="btngrp">
<input type="submit" class="btn submit" value="{{ isPaused ? 'Resume' : 'Pause' }}">
{% if isPaused %}
{% if pausedUntil %}
<strong>&nbsp;Paused until: {{ pausedUntil|date }}</strong>
{% else %}
<strong>&nbsp;Paused indefinitely</strong>
{% endif %}
{% else %}
for
<input class="text" type="text" name="for" placeholder="1 month" />
{% endif %}
</div>
</div>
</form>
</div>
TWIG;
return $view->renderString($markup, $context);
}
}
<?php
namespace modules\controllers;
use Craft;
use craft\commerce\elements\Subscription;
use craft\commerce\Plugin as Commerce;
use craft\commerce\stripe\gateways\PaymentIntents;
use craft\commerce\stripe\Plugin as StripePlugin;
use craft\web\Controller;
use DateTime;
use Exception;
use Stripe\Stripe;
use yii\base\InvalidConfigException;
/**
* Class SubscriptionController
*
* @author Ether Creative
* @package modules\controllers
*/
class SubscriptionsController extends Controller
{
public function __construct ($id, $module, $config = [])
{
parent::__construct($id, $module, $config);
Stripe::setApiKey(Craft::parseEnv(Commerce::getInstance()->getGateways()->getGatewayByHandle('CHANGE_ME')->apiKey));
Stripe::setAppInfo(StripePlugin::getInstance()->name, StripePlugin::getInstance()->version, StripePlugin::getInstance()->documentationUrl);
Stripe::setApiVersion(PaymentIntents::STRIPE_API_VERSION);
}
public function actionPause ()
{
$request = Craft::$app->getRequest();
$session = Craft::$app->getSession();
$uid = $request->getValidatedBodyParam('subscriptionUid');
$for = $request->getBodyParam('for');
$sub = Subscription::find()->anyStatus()->uid($uid)->one();
$data = $sub->getSubscriptionData();
$resumesAt = null;
if (!empty($for))
{
$d = new DateTime();
try {
$d = $d->modify($for);
} catch (Exception $e) {
$d = false;
Craft::error($e, 'B:SubscriptionPause');
}
if ($d === false)
{
$session->setError('Pause duration is invalid (see logs)!');
return $this->redirectToPostedUrl();
}
if ($d <= new DateTime())
{
$session->setError('Pause duration must result in the future!');
return $this->redirectToPostedUrl();
}
$resumesAt = $d->getTimestamp();
}
\Stripe\Subscription::update($data['id'], [
'pause_collection' => [
'behavior' => 'void',
'resumes_at' => $resumesAt,
],
]);
self::_refresh($sub);
return $this->redirectToPostedUrl();
}
public function actionResume ()
{
$uid = Craft::$app->getRequest()->getValidatedBodyParam('subscriptionUid');
$sub = Subscription::find()->anyStatus()->uid($uid)->one();
$data = $sub->getSubscriptionData();
\Stripe\Subscription::update($data['id'], [
'pause_collection' => '',
]);
self::_refresh($sub);
return $this->redirectToPostedUrl();
}
/**
* Refresh the subscription data
* (Bit of a hack, kinda shitty that we can't do it directly. You can remove
* this if you're only viewing the subscription in the CP)
*
* @param Subscription $subscription
*
* @throws InvalidConfigException
*/
private static function _refresh (Subscription $subscription)
{
$subscription->getGateway()->getHasBillingIssues($subscription);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment