Skip to content

Commit

Permalink
Merge pull request #211 from roodjong/subscription-message-member-id
Browse files Browse the repository at this point in the history
Allow usage of member number in subscription message
  • Loading branch information
pingiun authored Nov 17, 2024
2 parents 25dc7cd + 696ac58 commit 1a0360d
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 62 deletions.
2 changes: 2 additions & 0 deletions config/instances/ds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ contribution:
signup:
min_age: 16
max_age: null

mollie_payment_description: "Contributiebetaling {{organisation_name}}, voor lidnummer {{member_number}}"
2 changes: 2 additions & 0 deletions config/instances/rood.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ contribution:
signup:
min_age: 14
max_age: 27

mollie_payment_description: "Contributiebetaling {{organisation_name}}, voor lidnummer {{member_number}}"
87 changes: 87 additions & 0 deletions src/Command/MigrateSubscriptionToNewMessage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

declare(strict_types = 1);

namespace App\Command;

use App\Entity\Member;
use App\Service\SubscriptionSetupService;
use Doctrine\ORM\EntityManagerInterface;
use Mollie\Api\Exceptions\ApiException;
use Mollie\Api\MollieApiClient;
use Mollie\Api\Resources\Subscription;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;

class MigrateSubscriptionToNewMessage extends Command
{
protected static $defaultName = 'app:migrate-subscription-to-new-message';
protected static $defaultDescription = 'Update all Mollie subscriptions to use the currently configured payment description';

public function __construct(
private readonly MollieApiClient $mollieApiClient,
private readonly EntityManagerInterface $entityManager,
private readonly SubscriptionSetupService $subscriptionService,
)
{
parent::__construct();
}

public function execute(InputInterface $input, OutputInterface $output): int
{
$output->writeln('Updating Mollie subscription to new message');

try {
$this->confirmChangeCount($input, $output);
} catch (\Exception $e) {
$output->writeln($e->getMessage());
return Command::FAILURE;
}

$memberRepository = $this->entityManager->getRepository(Member::class);

$allSubscriptions = $this->mollieApiClient->subscriptions->iterator();
foreach ($allSubscriptions as $subscription) {
/** @var Subscription $subscription */
if (!$subscription->isActive()) {
continue;
}
$output->writeln('Updating subscription ' . $subscription->description);
/** @var Member $member */
$members = $memberRepository->findBy(['mollieSubscriptionId' => $subscription->id]);
if (count($members) === 0) {
$output->writeln('<comment>Could not find member for subscription ' . $subscription->id . '! (skipping)</comment>');
continue;
}
$member = $members[0];
$newDescription = $this->subscriptionService->generateDescription($member);
$subscription->description = $newDescription;
try {
$subscription->update();
} catch (ApiException $exception) {
$output->writeln('<error>' . $exception->getMessage() . '</error>');
}
}

return Command::SUCCESS;
}

/**
* @throws \Exception
*/
private function confirmChangeCount(InputInterface $input, OutputInterface $output): void
{
$allSubscriptions = $this->mollieApiClient->subscriptions->iterator();
$amount = $allSubscriptions->count();
$helper = $this->getHelper('question');
$question = new Question("You are about to change $amount descriptions, to continue confirm the amount: ", false);
$answer = (int)$helper->ask($input, $output, $question);
if ($answer === $amount) {
return;
}
throw new \Exception('Did not confirm');
}
}
44 changes: 14 additions & 30 deletions src/Controller/Admin/MembershipApplicationCrud.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Controller\Admin;

use App\Service\SubscriptionSetupService;
use Doctrine\ORM\QueryBuilder;

use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
Expand Down Expand Up @@ -31,7 +32,11 @@ class MembershipApplicationCrud extends AbstractCrudController
private MailerInterface $mailer;
private MollieApiClient $mollieApiClient;

public function __construct(MailerInterface $mailer, MollieApiClient $mollieApiClient)
public function __construct(
MailerInterface $mailer,
MollieApiClient $mollieApiClient,
private readonly SubscriptionSetupService $subscriptionService,
)
{
$this->mailer = $mailer;
$this->mollieApiClient = $mollieApiClient;
Expand Down Expand Up @@ -90,48 +95,27 @@ public function acceptApplication(AdminContext $context)
$organizationName = $this->getParameter('app.organizationName');
$mailer = $this->mailer;

/** @var MembershipApplication $application */
$application = $context->getEntity()->getInstance();

$mollieIntervals = [
Member::PERIOD_MONTHLY => '1 month',
Member::PERIOD_QUARTERLY => '3 months',
Member::PERIOD_ANNUALLY => '1 year'
];
$dateTimeIntervals = [
Member::PERIOD_MONTHLY => 'P1M',
Member::PERIOD_QUARTERLY => 'P3M',
Member::PERIOD_ANNUALLY => 'P1Y'
];

$startDate = new DateTime();
$startDate->setDate(date('Y'), floor(date('m') / 3) * 3, 1);
$startDate->add(new DateInterval($dateTimeIntervals[$application->getContributionPeriod()]));
$subscriptionId = null;
$member = $application->createMember(null);
$member->generateNewPasswordToken();
$em = $this->getDoctrine()->getManager();
$em->persist($member);
$em->flush();

try {
$customer = $this->mollieApiClient->customers->get($application->getMollieCustomerId());

$subscription = $customer->createSubscription([
'amount' => [
'currency' => 'EUR',
'value' => number_format($application->getContributionPerPeriodInEuros(), 2, '.', '')
],
'interval' => $mollieIntervals[$application->getContributionPeriod()],
'description' => $this->getParameter('mollie_payment_description'),
'startDate' => $startDate->format('Y-m-d'),
'webhookUrl' => $this->generateUrl('member_contribution_mollie_webhook', [], UrlGeneratorInterface::ABSOLUTE_URL)
]);
$subscription = $this->subscriptionService->createSubscription($member, $customer);
$subscriptionId = $subscription->id;
$member->setMollieSubscriptionId($subscriptionId);

} catch (ApiException $e) {
// De subscription moet later nog gedaan worden door het lid zelf
$this->addFlash("warning", "Het net geaccepteerde lid heeft nog geen automatisch incasso. Het nieuwe lid kan dit alleen zelf instellen.");
}

$member = $application->createMember($subscriptionId);
$member->generateNewPasswordToken();

$em = $this->getDoctrine()->getManager();
$em->persist($member);
$em->remove($application);
$em->flush();
Expand Down
50 changes: 19 additions & 31 deletions src/Controller/ContributionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Controller;

use App\Service\SubscriptionSetupService;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\{ Response, Request };
Expand All @@ -21,6 +22,12 @@
class ContributionController extends AbstractController
{

public function __construct(
private readonly SubscriptionSetupService $subscriptionService,
)
{
}

/**
* @Route("/contributie-instellingen", name="member_contribution_preferences")
*/
Expand Down Expand Up @@ -134,7 +141,9 @@ public function payContribution(Request $request, MollieApiClient $mollieApiClie
if ($automaticCollection)
{
return $this->render('user/contribution/first-payment.html.twig', [
'checkoutUrl' => $molliePayment->getCheckoutUrl()
'checkoutUrl' => $molliePayment->getCheckoutUrl(),
// The user is currently setting up contribution, so setting this false disables the contribution nagbar
'contributionEnabled' => false,
]);
}

Expand Down Expand Up @@ -176,7 +185,7 @@ public function webhook(Request $request, MollieApiClient $mollieApiClient): Res
$contributionPayment->setStatus(ContributionPayment::STATUS_PAID);

if ($member->getCreateSubscriptionAfterPayment()) {
$this->setupSubscription($member, $customer);
$this->subscriptionService->createSubscription($member, $customer);
$member->setCreateSubscriptionAfterPayment(false);
}
break;
Expand Down Expand Up @@ -225,6 +234,7 @@ public function automaticCollection(Request $request, LoggerInterface $logger):
*/
public function enableAutomaticCollection(Request $request, MollieApiClient $mollieApiClient): Response {
$em = $this->getDoctrine()->getManager();
/** @var Member $member */
$member = $this->getUser();

// Already has subscription
Expand All @@ -243,7 +253,7 @@ public function enableAutomaticCollection(Request $request, MollieApiClient $mol
}

// 3. If a mandate does exist, set up subscription
$this->setupSubscription($member, $customer);
$this->subscriptionService->createSubscription($member, $customer);

// 4. Show automatic collection enabled screen
return $this->redirectToRoute('member_contribution_automatic_collection');
Expand Down Expand Up @@ -445,23 +455,23 @@ private function getOrCreateMollieCustomer(MollieApiClient $mollieApiClient) {
return $customer;
}

private function createContributionPayment(int $contributionPeriod, float $contributionAmount, $molliePayment) {
private function createContributionPayment(int $contributionPeriod, float $contributionAmount, $molliePayment)
{
// Create contribution payment
$contributionPayment = new ContributionPayment();
$contributionPayment->setAmountInCents(round($contributionAmount * 100));
$contributionPayment->setStatus(ContributionPayment::STATUS_PENDING);
$contributionPayment->setPaymentTime(new DateTime);

if ($molliePayment !== null)
{
if ($molliePayment !== null) {
$contributionPayment->setMolliePaymentId($molliePayment->id);
}

// Set correct year and period information
$contributionPayment->setPeriodYear((int) date('Y'));
$contributionPayment->setPeriodYear((int)date('Y'));

$month = (int) date('m');
switch($contributionPeriod) {
$month = (int)date('m');
switch ($contributionPeriod) {
case Member::PERIOD_MONTHLY:
$contributionPayment->setPeriodMonthStart($month);
$contributionPayment->setPeriodMonthEnd($month);
Expand All @@ -479,26 +489,4 @@ private function createContributionPayment(int $contributionPeriod, float $contr

return $contributionPayment;
}

private function setupSubscription(Member $member, $customer) {
$startDate = DateTime::createFromFormat('Y-m-d', date('Y-'). (ceil(date('m') / 3) * 3 + 1). '-1');

$subscription = $customer->createSubscription([
'amount' => [
'currency' => 'EUR',
'value' => number_format($member->getContributionPerPeriodInEuros(), 2, '.', '')
],
'interval' => [
Member::PERIOD_MONTHLY => '1 month',
Member::PERIOD_QUARTERLY => '3 months',
Member::PERIOD_ANNUALLY => '1 year'
][$member->getContributionPeriod()],
'description' => $this->getParameter('mollie_payment_description'),
'startDate' => $startDate->format('Y-m-d'),
'webhookUrl' => $this->generateUrl('member_contribution_mollie_webhook', [], UrlGeneratorInterface::ABSOLUTE_URL)
]);
$member->setMollieSubscriptionId($subscription->id);
$this->getDoctrine()->getManager()->flush();
}

}
2 changes: 1 addition & 1 deletion src/Controller/MemberController.php
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ public function details(Request $request, UserPasswordEncoderInterface $password
$member = $this->getUser();
if (!$member->getAcceptUsePersonalInformation())
return $this->memberAcceptPersonalDetails($request);
$contributionEnabled = $this->getParameter('app.contributionEnabled');
$contributionEnabled = $this->getParameter('app.contributionEnabled');
$form = $this->createForm(MemberDetailsType::class, $member);
$revision = new MemberDetailsRevision($member, true);
$success = false;
Expand Down
73 changes: 73 additions & 0 deletions src/Service/SubscriptionSetupService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

declare(strict_types = 1);

namespace App\Service;

use App\Entity\Member;
use DateTime;
use Doctrine\Persistence\ManagerRegistry;
use Mollie\Api\Exceptions\ApiException;
use Mollie\Api\Resources\Customer;
use Mollie\Api\Resources\Subscription;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Yaml\Yaml;

class SubscriptionSetupService
{
public function __construct(
private readonly ManagerRegistry $doctrine,
private readonly ParameterBagInterface $params,
private readonly UrlGeneratorInterface $router,
)
{
}

public function generateDescription(Member $member): string
{
$projectRoot = $this->params->get('kernel.project_dir');
$org_config = Yaml::parseFile($projectRoot . '/config/instances/' . $this->params->get('app.organizationID') . '.yaml');

$description = $org_config['mollie_payment_description'];
$description = str_replace('{{organisation_name}}', $this->params->get("app.organizationName"), $description);
return str_replace('{{member_number}}', (string)$member->getId(), $description);
}

/**
* @throws ApiException
*/
public function createSubscription(Member $member, Customer $customer): Subscription
{
$mollieIntervals = [
Member::PERIOD_MONTHLY => '1 month',
Member::PERIOD_QUARTERLY => '3 months',
Member::PERIOD_ANNUALLY => '1 year'
];
$dateTimeIntervals = [
Member::PERIOD_MONTHLY => 'P1M',
Member::PERIOD_QUARTERLY => 'P3M',
Member::PERIOD_ANNUALLY => 'P1Y'
];

$startDate = new DateTime();
$startDate->setDate((int)date('Y'), (int)floor(date('m') / 3) * 3, 1);
$startDate->add(new \DateInterval($dateTimeIntervals[$member->getContributionPeriod()]));

$description = $this->generateDescription($member);

$subscription = $customer->createSubscription([
'amount' => [
'currency' => 'EUR',
'value' => number_format($member->getContributionPerPeriodInEuros(), 2, '.', '')
],
'interval' => $mollieIntervals[$member->getContributionPeriod()],
'description' => $description,
'startDate' => $startDate->format('Y-m-d'),
'webhookUrl' => $this->router->generate('member_contribution_mollie_webhook', [], UrlGeneratorInterface::ABSOLUTE_URL)
]);
$member->setMollieSubscriptionId($subscription->id);
$this->doctrine->getManager()->flush();
return $subscription;
}
}

0 comments on commit 1a0360d

Please sign in to comment.