Merge branch '799-subscription-support' into 809-loyalty-system-support
This commit is contained in:
commit
857c573ae5
20 changed files with 1073 additions and 61 deletions
|
|
@ -317,4 +317,41 @@ apiv2_insurance_body_types:
|
|||
apiv2_loyalty_register:
|
||||
path: /apiv2/loyalty/register
|
||||
controller: App\Controller\CustomerAppAPI\LoyaltyController::register
|
||||
methods: [POST]
|
||||
methods: [POST]
|
||||
|
||||
# static content
|
||||
apiv2_static_content:
|
||||
path: /apiv2/static_content/{id}
|
||||
controller: App\Controller\CustomerAppAPI\StaticContentController::getContent
|
||||
methods: [GET]
|
||||
|
||||
# subscription
|
||||
apiv2_subscription_plan_details:
|
||||
path: /apiv2/subscription/vehicle/{vid}/plan
|
||||
controller: App\Controller\CustomerAppAPI\SubscriptionController::getPlanDetails
|
||||
methods: [GET]
|
||||
|
||||
#apiv2_subscription_paymongo_public_key:
|
||||
# path: /apiv2/subscription/ppk
|
||||
# controller: App\Controller\CustomerAppAPI\SubscriptionController::getPaymongoPublicKey
|
||||
# methods: [GET]
|
||||
|
||||
apiv2_subscription_create:
|
||||
path: /apiv2/subscription
|
||||
controller: App\Controller\CustomerAppAPI\SubscriptionController::createSubscription
|
||||
methods: [POST]
|
||||
|
||||
apiv2_subscription_finalize:
|
||||
path: /apiv2/subscription/{id}/finalize
|
||||
controller: App\Controller\CustomerAppAPI\SubscriptionController::finalizeSubscription
|
||||
methods: [GET]
|
||||
|
||||
#apiv2_subscription_payment_intent:
|
||||
# path: /apiv2/subscription/payment_intent/{pi_id}
|
||||
# controller: App\Controller\CustomerAppAPI\SubscriptionController::getPaymentIntent
|
||||
# methods: [GET]
|
||||
|
||||
#apiv2_subscription_activate:
|
||||
# path: /apiv2/subscription/{id}/activate
|
||||
# controller: App\Controller\CustomerAppAPI\SubscriptionController::activateSubscription
|
||||
# methods: [POST]
|
||||
|
|
|
|||
|
|
@ -17,6 +17,13 @@ parameters:
|
|||
ios_app_version: "%env(IOS_APP_VERSION)%"
|
||||
insurance_premiums_banner_url: "%env(INSURANCE_PREMIUMS_BANNER_URL)%"
|
||||
enabled_hub_filters: "%env(ENABLED_HUB_FILTERS)%"
|
||||
insurance_paymongo_public_key: "%env(INSURANCE_PAYMONGO_PUBLIC_KEY)%"
|
||||
insurance_paymongo_secret_key: "%env(INSURANCE_PAYMONGO_SECRET_KEY)%"
|
||||
insurance_paymongo_webhook_id: "%env(INSURANCE_PAYMONGO_WEBHOOK_ID)%"
|
||||
subscription_paymongo_public_key: "%env(SUBSCRIPTION_PAYMONGO_PUBLIC_KEY)%"
|
||||
subscription_paymongo_secret_key: "%env(SUBSCRIPTION_PAYMONGO_SECRET_KEY)%"
|
||||
subscription_paymongo_webhook_id: "%env(SUBSCRIPTION_PAYMONGO_WEBHOOK_ID)%"
|
||||
subscription_months: "%env(SUBSCRIPTION_MONTHS)%"
|
||||
|
||||
services:
|
||||
# default configuration for services in *this* file
|
||||
|
|
@ -114,7 +121,6 @@ services:
|
|||
arguments:
|
||||
$em: "@doctrine.orm.entity_manager"
|
||||
$paymongo: "@App\\Service\\PayMongoConnector"
|
||||
$webhook_id: "%env(PAYMONGO_WEBHOOK_ID)%"
|
||||
|
||||
# rider tracker service
|
||||
App\Service\RiderTracker:
|
||||
|
|
@ -238,8 +244,6 @@ services:
|
|||
App\Service\PayMongoConnector:
|
||||
arguments:
|
||||
$base_url: "%env(PAYMONGO_BASE_URL)%"
|
||||
$public_key: "%env(PAYMONGO_PUBLIC_KEY)%"
|
||||
$secret_key: "%env(PAYMONGO_SECRET_KEY)%"
|
||||
|
||||
# loyalty system connector
|
||||
App\Service\LoyaltyConnector:
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use Symfony\Component\Console\Command\Command;
|
|||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
|
|
@ -19,14 +20,18 @@ class ProcessLatePaymongoTransactionsCommand extends Command
|
|||
{
|
||||
protected $em;
|
||||
protected $paymongo;
|
||||
|
||||
protected $webhook_id;
|
||||
|
||||
public function __construct(EntityManagerInterface $em, PayMongoConnector $paymongo, $webhook_id)
|
||||
public function __construct(EntityManagerInterface $em, PayMongoConnector $paymongo, ParameterBagInterface $params)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->webhook_id = $params->get('insurance_paymongo_webhook_id');
|
||||
|
||||
$this->paymongo = $paymongo;
|
||||
$this->webhook_id = $webhook_id;
|
||||
$this->paymongo->initialize(
|
||||
$params->get('insurance_paymongo_public_key'),
|
||||
$params->get('insurance_paymongo_secret_key')
|
||||
);
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,24 @@ use Symfony\Component\HttpFoundation\Request;
|
|||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use App\Service\PayMongoConnector;
|
||||
|
||||
use Catalyst\MenuBundle\Annotation\Menu;
|
||||
|
||||
class BatterySizeController extends Controller
|
||||
{
|
||||
protected $pm;
|
||||
|
||||
public function __construct(PayMongoConnector $pm, ParameterBagInterface $params)
|
||||
{
|
||||
$this->pm = $pm;
|
||||
$this->pm->initialize(
|
||||
$params->get('subscription_paymongo_public_key'),
|
||||
$params->get('subscription_paymongo_secret_key'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Menu(selected="bsize_list")
|
||||
*/
|
||||
|
|
@ -130,7 +143,8 @@ class BatterySizeController extends Controller
|
|||
->setTIPriceMotolite($req->request->get('tip_motolite'))
|
||||
->setTIPricePremium($req->request->get('tip_premium'))
|
||||
->setTIPriceOther($req->request->get('tip_other'))
|
||||
->setTIPriceLazada($req->request->get('tip_lazada'));
|
||||
->setTIPriceLazada($req->request->get('tip_lazada'))
|
||||
->setSubRecurringFee($req->request->get('sub_recurring_fee'));
|
||||
}
|
||||
|
||||
public function addSubmit(Request $req, ValidatorInterface $validator)
|
||||
|
|
@ -167,6 +181,9 @@ class BatterySizeController extends Controller
|
|||
$em->persist($row);
|
||||
$em->flush();
|
||||
|
||||
// create new paymongo subscription plan
|
||||
$this->pm->createOrUpdateSubPlan($row);
|
||||
|
||||
// return successful response
|
||||
return $this->json([
|
||||
'success' => 'Changes have been saved!'
|
||||
|
|
@ -234,6 +251,9 @@ class BatterySizeController extends Controller
|
|||
// validated! save the entity
|
||||
$em->flush();
|
||||
|
||||
// find if paymongo subscription plan exists, then update accordingly
|
||||
$this->pm->createOrUpdateSubPlan($row);
|
||||
|
||||
// return successful response
|
||||
return $this->json([
|
||||
'success' => 'Changes have been saved!'
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use App\Ramcar\JOStatus;
|
|||
use App\Entity\Warranty;
|
||||
use App\Entity\JobOrder;
|
||||
use App\Entity\CustomerSession;
|
||||
use App\Service\PayMongoConnector;
|
||||
|
||||
class ApiController extends BaseApiController
|
||||
{
|
||||
|
|
@ -164,4 +165,12 @@ class ApiController extends BaseApiController
|
|||
{
|
||||
return 'Our services are currently limited to some areas in Metro Manila, Baguio, Batangas, Laguna, Cavite, Pampanga, and Palawan. We will update you as soon as we are available in your area. Thank you for understanding. Keep safe!';
|
||||
}
|
||||
|
||||
protected function initializeSubscriptionPayMongoConnector(PayMongoConnector &$pm)
|
||||
{
|
||||
$pm->initialize(
|
||||
$this->getParameter('subscription_paymongo_public_key'),
|
||||
$this->getParameter('subscription_paymongo_secret_key'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use App\Ramcar\CustomerSource;
|
|||
use App\Entity\Customer;
|
||||
use App\Entity\PrivacyPolicy;
|
||||
use App\Service\HashGenerator;
|
||||
use App\Service\PayMongoConnector;
|
||||
|
||||
class CustomerController extends ApiController
|
||||
{
|
||||
|
|
@ -43,13 +44,10 @@ class CustomerController extends ApiController
|
|||
]);
|
||||
}
|
||||
|
||||
public function updateInfo(Request $req)
|
||||
public function updateInfo(Request $req, PayMongoConnector $pm)
|
||||
{
|
||||
// validate params
|
||||
$validity = $this->validateRequest($req, [
|
||||
'first_name',
|
||||
'last_name',
|
||||
]);
|
||||
$validity = $this->validateRequest($req);
|
||||
|
||||
if (!$validity['is_valid']) {
|
||||
return new ApiResponse(false, $validity['error']);
|
||||
|
|
@ -67,6 +65,12 @@ class CustomerController extends ApiController
|
|||
|
||||
$this->em->flush();
|
||||
|
||||
// initialize paymongo connector
|
||||
$this->initializeSubscriptionPayMongoConnector($pm);
|
||||
|
||||
// update customer paymongo record if exists
|
||||
$pm->updateCustomerIfExists($cust);
|
||||
|
||||
// response
|
||||
return new ApiResponse();
|
||||
}
|
||||
|
|
@ -129,10 +133,21 @@ class CustomerController extends ApiController
|
|||
$this->session->setCustomer($cust);
|
||||
}
|
||||
|
||||
$cust->setFirstName($req->request->get('first_name'))
|
||||
->setLastName($req->request->get('last_name'))
|
||||
->setEmail($req->request->get('email', ''))
|
||||
->setConfirmed($this->session->isConfirmed());
|
||||
if (!is_null($req->request->get('first_name'))) {
|
||||
$cust->setFirstName($req->request->get('first_name'));
|
||||
}
|
||||
|
||||
if (!is_null($req->request->get('last_name'))) {
|
||||
$cust->setLastName($req->request->get('last_name'));
|
||||
}
|
||||
|
||||
if (!is_null($req->request->get('email'))) {
|
||||
$cust->setEmail($req->request->get('email'));
|
||||
}
|
||||
|
||||
if (!is_null($this->session->isConfirmed())) {
|
||||
$cust->setConfirmed($this->session->isConfirmed());
|
||||
}
|
||||
|
||||
// if customer user isn't set, set it now
|
||||
if ($cust->getCustomerUser() == null) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Controller\CustomerAppAPI;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Catalyst\ApiBundle\Component\Response as ApiResponse;
|
||||
|
|
@ -33,7 +34,7 @@ class InsuranceController extends ApiController
|
|||
$this->client = $client;
|
||||
}
|
||||
|
||||
public function createApplication(Request $req, PayMongoConnector $paymongo, UrlGeneratorInterface $router)
|
||||
public function createApplication(Request $req, PayMongoConnector $paymongo, UrlGeneratorInterface $router, ParameterBagInterface $params)
|
||||
{
|
||||
// validate params
|
||||
$validity = $this->validateRequest($req, [
|
||||
|
|
@ -162,6 +163,12 @@ class InsuranceController extends ApiController
|
|||
$this->em->persist($gt);
|
||||
$this->em->flush();
|
||||
|
||||
// initialize paymongo connector
|
||||
$paymongo->initialize(
|
||||
$params->get('insurance_paymongo_public_key'),
|
||||
$params->get('insurance_paymongo_secret_key')
|
||||
);
|
||||
|
||||
// create paymongo checkout resource
|
||||
$checkout = $paymongo->createCheckout(
|
||||
$cust,
|
||||
|
|
|
|||
34
src/Controller/CustomerAppAPI/StaticContentController.php
Normal file
34
src/Controller/CustomerAppAPI/StaticContentController.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller\CustomerAppAPI;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Catalyst\ApiBundle\Component\Response as ApiResponse;
|
||||
|
||||
use App\Entity\StaticContent;
|
||||
|
||||
class StaticContentController extends ApiController
|
||||
{
|
||||
public function getContent(Request $req, $id)
|
||||
{
|
||||
// check requirements
|
||||
$validity = $this->validateRequest($req);
|
||||
|
||||
if (!$validity['is_valid']) {
|
||||
return new ApiResponse(false, $validity['error']);
|
||||
}
|
||||
|
||||
// get content
|
||||
$content = $this->em->getRepository(Staticcontent::class)->find($id);
|
||||
|
||||
// check if it exists
|
||||
if ($content == null) {
|
||||
return new ApiResponse(false, 'Requested content does not exist.');
|
||||
}
|
||||
|
||||
// response
|
||||
return new ApiResponse(true, '', [
|
||||
'content' => $content->getContent(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
315
src/Controller/CustomerAppAPI/SubscriptionController.php
Normal file
315
src/Controller/CustomerAppAPI/SubscriptionController.php
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller\CustomerAppAPI;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Catalyst\ApiBundle\Component\Response as ApiResponse;
|
||||
use App\Service\PayMongoConnector;
|
||||
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\Vehicle;
|
||||
use App\Entity\Subscription;
|
||||
use App\Entity\CustomerVehicle;
|
||||
use App\Ramcar\SubscriptionStatus;
|
||||
|
||||
use DateTime;
|
||||
|
||||
class SubscriptionController extends ApiController
|
||||
{
|
||||
public function getPlanDetails(Request $req, $vid, PayMongoConnector $pm)
|
||||
{
|
||||
// check requirements
|
||||
$validity = $this->validateRequest($req);
|
||||
|
||||
if (!$validity['is_valid']) {
|
||||
return new ApiResponse(false, $validity['error']);
|
||||
}
|
||||
|
||||
// get vehicle
|
||||
$vehicle = $this->em->getRepository(Vehicle::class)->find($vid);
|
||||
if ($vehicle == null) {
|
||||
return new ApiResponse(false, 'Invalid vehicle.');
|
||||
}
|
||||
|
||||
$plan = null;
|
||||
|
||||
// get compatible batteries
|
||||
$batts = $vehicle->getActiveBatteries();
|
||||
|
||||
if (!empty($batts)) {
|
||||
// initialize paymongo connector
|
||||
$this->initializeSubscriptionPayMongoConnector($pm);
|
||||
|
||||
$plan = $pm->getPlanByBatterySize($batts[0]->getSize());
|
||||
}
|
||||
|
||||
error_log("FOUND PLAN FOR $vid: " . print_r($plan, true));
|
||||
|
||||
// response
|
||||
return new ApiResponse(true, '', [
|
||||
'plan' => $plan,
|
||||
]);
|
||||
}
|
||||
|
||||
// NOTE: disabling this since we can just include the public key with the subscription creation endpoint
|
||||
/*
|
||||
public function getPayMongoPublicKey(Request $req)
|
||||
{
|
||||
// check requirements
|
||||
$validity = $this->validateRequest($req);
|
||||
|
||||
if (!$validity['is_valid']) {
|
||||
return new ApiResponse(false, $validity['error']);
|
||||
}
|
||||
|
||||
// response
|
||||
return new ApiResponse(true, '', [
|
||||
'key' => $this->getParameter('subscription_paymongo_public_key'),
|
||||
]);
|
||||
}
|
||||
*/
|
||||
|
||||
public function createSubscription(Request $req, PayMongoConnector $pm)
|
||||
{
|
||||
// check requirements
|
||||
$validity = $this->validateRequest($req, [
|
||||
'plan_id',
|
||||
'cv_id',
|
||||
'email',
|
||||
]);
|
||||
|
||||
if (!$validity['is_valid']) {
|
||||
return new ApiResponse(false, $validity['error']);
|
||||
}
|
||||
|
||||
// get customer
|
||||
$cust = $this->session->getCustomer();
|
||||
if ($cust == null) {
|
||||
return new ApiResponse(false, 'No customer information found.');
|
||||
}
|
||||
|
||||
// verify email does not belong to someone else
|
||||
$email = $req->request->get('email');
|
||||
$qb = $this->em->getRepository(Customer::class)
|
||||
->createQueryBuilder('c')
|
||||
->select('c')
|
||||
->where('c.email = :email')
|
||||
->andWhere('c.id != :cust_id')
|
||||
->setParameter('email', $email)
|
||||
->setParameter('cust_id', $cust->getID());
|
||||
|
||||
$email_exists = $qb->getQuery()->getOneOrNullResult();
|
||||
if (!empty($email_exists)) {
|
||||
return new ApiResponse(false, 'Email is already in use. Please use a different email address.');
|
||||
}
|
||||
|
||||
// get customer vehicle
|
||||
$cv = $this->em->getRepository(CustomerVehicle::class)->find($req->request->get('cv_id'));
|
||||
|
||||
// check if it exists
|
||||
if ($cv == null) {
|
||||
return new ApiResponse(false, 'Vehicle does not exist.');
|
||||
}
|
||||
|
||||
// check if it's owned by customer
|
||||
if ($cv->getCustomer()->getID() != $cust->getID()) {
|
||||
return new ApiResponse(false, 'Invalid vehicle.');
|
||||
}
|
||||
|
||||
// initialize paymongo connector
|
||||
$this->initializeSubscriptionPayMongoConnector($pm);
|
||||
|
||||
// get paymongo customer
|
||||
$pm_cust = $pm->findOrCreateCustomer($email, $cust);
|
||||
if (empty($pm_cust)) {
|
||||
return new ApiResponse(false, 'Error retrieving customer record. Please try again later.');
|
||||
}
|
||||
|
||||
// create subscription
|
||||
// NOTE: for now we save ourselves the extra API call and assume the plan_id is valid since this won't change often anyway
|
||||
$pm_sub = $pm->createSubscription($pm_cust['id'], $req->request->get('plan_id'));
|
||||
$sub_pi = $pm_sub['response']['data']['attributes']['latest_invoice']['payment_intent'] ?? null;
|
||||
|
||||
// not the response we expected
|
||||
if (empty($sub_pi)) {
|
||||
return new ApiResponse(false, 'Error creating subscription. Please try again later.');
|
||||
}
|
||||
|
||||
// the payment intent must still be in a pending state
|
||||
// TODO: log this somewhere
|
||||
if ($sub_pi['status'] !== 'awaiting_payment_method') {
|
||||
return new ApiResponse(false, 'Error creating subscription invoice. Please try again later.');
|
||||
}
|
||||
|
||||
// fetch payment intent details for client key
|
||||
$pi = $pm->getPaymentIntent($sub_pi['id']);
|
||||
if (empty($pi['response']['data']['id'])) {
|
||||
return new ApiResponse(false, 'Error retrieving payment intent. Please try again later.');
|
||||
}
|
||||
|
||||
// create subscription entity
|
||||
$obj = new Subscription();
|
||||
$obj->setCustomer($cust)
|
||||
->setCustomerVehicle($cv)
|
||||
->setEmail($email)
|
||||
->setStatus(SubscriptionStatus::PENDING)
|
||||
->setExtApiId($pm_sub['response']['data']['id'])
|
||||
->setMetadata($pm_sub['response']['data']);
|
||||
|
||||
// if requested to save email, save it
|
||||
if (!empty($req->request->get('remember_email'))) {
|
||||
$cust->setEmail($email);
|
||||
}
|
||||
|
||||
// save stuff to db
|
||||
$this->em->persist($obj);
|
||||
$this->em->flush();
|
||||
|
||||
// response
|
||||
return new ApiResponse(true, '', [
|
||||
'subscription_id' => $obj->getID(),
|
||||
'payment_intent_id' => $pi['response']['data']['id'],
|
||||
'payment_intent_client_key' => $pi['response']['data']['attributes']['client_key'],
|
||||
'paymongo_public_key' => $this->getParameter('subscription_paymongo_public_key'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function finalizeSubscription(Request $req, $id, PayMongoConnector $pm)
|
||||
{
|
||||
// check requirements
|
||||
$validity = $this->validateRequest($req);
|
||||
|
||||
if (!$validity['is_valid']) {
|
||||
return new ApiResponse(false, $validity['error']);
|
||||
}
|
||||
|
||||
// initialize paymongo connector
|
||||
$this->initializeSubscriptionPayMongoConnector($pm);
|
||||
|
||||
// get subscription
|
||||
$sub_obj = $this->em->getRepository(Subscription::class)->findOneBy([
|
||||
'id' => $id,
|
||||
'status' => SubscriptionStatus::PENDING,
|
||||
'customer' => $this->session->getCustomer(),
|
||||
]);
|
||||
|
||||
if (empty($sub_obj)) {
|
||||
return new ApiResponse(false, 'Invalid subscription provided.');
|
||||
}
|
||||
|
||||
// get paymongo subscription so we can verify if the latest invoice is paid or not
|
||||
$pm_sub = $pm->getSubscription($sub_obj->getExtApiId());
|
||||
if (empty($pm_sub['response']['data']['id'])) {
|
||||
return new ApiResponse(false, 'Error retrieving subscription. Please try again later.');
|
||||
}
|
||||
|
||||
// make sure the latest invoice has been paid
|
||||
// NOTE: ignore this, this is unreliable due to race condition
|
||||
/*
|
||||
if ($pm_sub['response']['data']['attributes']['latest_invoice']['status'] !== 'paid') {
|
||||
return new ApiResponse(false, 'Latest invoice for subscription is not yet paid.');
|
||||
}
|
||||
*/
|
||||
|
||||
// get payment intent
|
||||
$pi = $pm->getPaymentIntent($pm_sub['response']['data']['attributes']['latest_invoice']['payment_intent']['id']);
|
||||
if (empty($pi['response']['data']['id'])) {
|
||||
return new ApiResponse(false, 'Error retrieving payment intent. Please try again later.');
|
||||
}
|
||||
|
||||
// if the paymongo sub is active, and the payment was successful, update the internal subscription status
|
||||
if (
|
||||
$sub_obj->getStatus() === SubscriptionStatus::PENDING &&
|
||||
$pi['response']['data']['attributes']['status'] === 'succeeded'
|
||||
) {
|
||||
$sub_start_date = new DateTime();
|
||||
$sub_end_date = clone $sub_start_date;
|
||||
$sub_end_date->modify('+' . $this->getParameter('subscription_months') . ' month');
|
||||
|
||||
$sub_obj->setStatus(SubscriptionStatus::ACTIVE)
|
||||
->setDateStart($sub_start_date)
|
||||
->setDateEnd($sub_end_date);
|
||||
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
error_log("PI STATUS: " . $pi['response']['data']['attributes']['status']);
|
||||
|
||||
// response
|
||||
return new ApiResponse(true, '', [
|
||||
'payment_intent' => $pi['response']['data'],
|
||||
]);
|
||||
}
|
||||
|
||||
/*
|
||||
public function getPaymentIntent(Request $req, $pid, PayMongoConnector $pm)
|
||||
{
|
||||
// check requirements
|
||||
$validity = $this->validateRequest($req);
|
||||
|
||||
if (!$validity['is_valid']) {
|
||||
return new ApiResponse(false, $validity['error']);
|
||||
}
|
||||
|
||||
// initialize paymongo connector
|
||||
$this->initializeSubscriptionPayMongoConnector($pm);
|
||||
|
||||
// get payment intent
|
||||
$pi = $pm->getPaymentIntent($pid);
|
||||
if (empty($pi['response']['data']['id'])) {
|
||||
return new ApiResponse(false, 'Error retrieving payment intent. Please try again later.');
|
||||
}
|
||||
|
||||
// response
|
||||
return new ApiResponse(true, '', [
|
||||
'payment_intent' => $pi['response']['data'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function activateSubscription(Request $req, $id, PayMongoConnector $pm)
|
||||
{
|
||||
// check requirements
|
||||
$validity = $this->validateRequest($req);
|
||||
|
||||
if (!$validity['is_valid']) {
|
||||
return new ApiResponse(false, $validity['error']);
|
||||
}
|
||||
|
||||
// initialize paymongo connector
|
||||
$this->initializeSubscriptionPayMongoConnector($pm);
|
||||
|
||||
// get subscription
|
||||
$obj = $this->em->getRepository(Subscription::class)->findOneBy([
|
||||
'id' => $id,
|
||||
'status' => SubscriptionStatus::PENDING,
|
||||
'customer' => $this->session->getCustomer(),
|
||||
]);
|
||||
|
||||
if (empty($obj)) {
|
||||
return new ApiResponse(false, 'Invalid subscription provided.');
|
||||
}
|
||||
|
||||
// get paymongo subscription so we can verify if the latest invoice is paid or not
|
||||
$pm_sub = $pm->getSubscription($obj->getExtApiId());
|
||||
if (empty($pm_sub['response']['data']['id'])) {
|
||||
return new ApiResponse(false, 'Error retrieving subscription. Please try again later.');
|
||||
}
|
||||
|
||||
// make sure the latest invoice has been paid
|
||||
if ($pm_sub['response']['data']['attributes']['latest_invoice']['status'] !== 'succeeded') {
|
||||
return new ApiResponse(false, 'Latest invoice for subscription is not yet paid.');
|
||||
}
|
||||
|
||||
// mark subscription as paid
|
||||
$obj->setStatus(SubscriptionStatus::ACTIVE)
|
||||
->setDateStart(new DateTime());
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
// response
|
||||
return new ApiResponse(true, '', [
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Controller\CustomerAppAPI;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Catalyst\ApiBundle\Component\Response as ApiResponse;
|
||||
use CrEOF\Spatial\PHP\Types\Geometry\Point;
|
||||
|
||||
|
|
@ -15,6 +16,7 @@ use App\Ramcar\JOStatus;
|
|||
use App\Ramcar\ServiceType;
|
||||
use App\Ramcar\TradeInType;
|
||||
use App\Ramcar\InsuranceApplicationStatus;
|
||||
use App\Ramcar\SubscriptionStatus;
|
||||
use App\Service\PayMongoConnector;
|
||||
use App\Service\PriceTierManager;
|
||||
use DateTime;
|
||||
|
|
@ -115,7 +117,7 @@ class VehicleController extends ApiController
|
|||
|
||||
}
|
||||
|
||||
public function getVehicle(Request $req, $id, PayMongoConnector $paymongo)
|
||||
public function getVehicle(Request $req, $id, PayMongoConnector $paymongo, ParameterBagInterface $params)
|
||||
{
|
||||
// check requirements
|
||||
$validity = $this->validateRequest($req);
|
||||
|
|
@ -139,7 +141,7 @@ class VehicleController extends ApiController
|
|||
|
||||
// response
|
||||
return new ApiResponse(true, '', [
|
||||
'vehicle' => $this->generateVehicleInfo($cv, true, $paymongo),
|
||||
'vehicle' => $this->generateVehicleInfo($cv, true, true, $paymongo, $params),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -210,7 +212,7 @@ class VehicleController extends ApiController
|
|||
]);
|
||||
}
|
||||
|
||||
public function listVehicles(Request $req, PayMongoConnector $paymongo)
|
||||
public function listVehicles(Request $req, PayMongoConnector $paymongo, ParameterBagInterface $params)
|
||||
{
|
||||
// validate params
|
||||
$validity = $this->validateRequest($req);
|
||||
|
|
@ -231,7 +233,7 @@ class VehicleController extends ApiController
|
|||
// only get the customer's vehicles whose flag_active is true
|
||||
$cvs = $this->em->getRepository(CustomerVehicle::class)->findBy(['flag_active' => true, 'customer' => $cust]);
|
||||
foreach ($cvs as $cv) {
|
||||
$cv_list[] = $this->generateVehicleInfo($cv, true, $paymongo);
|
||||
$cv_list[] = $this->generateVehicleInfo($cv, true, true, $paymongo, $params);
|
||||
}
|
||||
|
||||
// response
|
||||
|
|
@ -393,7 +395,7 @@ class VehicleController extends ApiController
|
|||
// TODO: possibly refactor this bit
|
||||
// if no valid previous JO is found, base the trade-in value on recommended batteries
|
||||
if (!$previous_jo_found) {
|
||||
$comp_batteries = $cv->getVehicle()->getBatteries();
|
||||
$comp_batteries = $cv->getVehicle()->getActiveBatteries();
|
||||
|
||||
// get the lowest trade-in value from the list of batteries
|
||||
if (!empty($comp_batteries)) {
|
||||
|
|
@ -417,7 +419,7 @@ class VehicleController extends ApiController
|
|||
];
|
||||
}
|
||||
|
||||
protected function generateVehicleInfo(CustomerVehicle $cv, $include_insurance = false, PayMongoConnector $paymongo)
|
||||
protected function generateVehicleInfo(CustomerVehicle $cv, $include_insurance = false, $include_active_sub = false, PayMongoConnector $paymongo, ParameterBagInterface $params)
|
||||
{
|
||||
$battery_id = null;
|
||||
if ($cv->getCurrentBattery() != null)
|
||||
|
|
@ -437,6 +439,8 @@ class VehicleController extends ApiController
|
|||
'cv_id' => $cv->getID(),
|
||||
'mfg_id' => $cv->getVehicle()->getManufacturer()->getID(),
|
||||
'make_id' => $cv->getVehicle()->getID(),
|
||||
'mfg_name' => $cv->getVehicle()->getManufacturer()->getName(),
|
||||
'make_name' => $cv->getVehicle()->getMake(),
|
||||
'name' => $cv_name,
|
||||
'plate_num' => $cv->getPlateNumber(),
|
||||
'model_year' => $cv->getModelYear(),
|
||||
|
|
@ -467,6 +471,12 @@ class VehicleController extends ApiController
|
|||
// TODO: maybe handle this more elegantly. issue is not sure it is a good idea to update the db for this very transient status as the webhook listener also updates this status right away
|
||||
switch ($status) {
|
||||
case InsuranceApplicationStatus::CREATED:
|
||||
// initialize paymongo connector
|
||||
$paymongo->initialize(
|
||||
$params->get('insurance_paymongo_public_key'),
|
||||
$params->get('insurance_paymongo_secret_key')
|
||||
);
|
||||
|
||||
// get latest status on this checkout from paymongo
|
||||
$checkout = $paymongo->getCheckout($gt->getExtTransactionId());
|
||||
|
||||
|
|
@ -506,6 +516,17 @@ class VehicleController extends ApiController
|
|||
$row['latest_insurance'] = $insurance;
|
||||
}
|
||||
|
||||
// get active subscription row
|
||||
if ($include_active_sub) {
|
||||
$active_sub = null;
|
||||
$sobj = $cv->getLatestSubscription();
|
||||
if (!empty($sobj) && $sobj->getStatus() == SubscriptionStatus::ACTIVE) {
|
||||
$active_sub = $sobj;
|
||||
}
|
||||
|
||||
$row['active_subscription'] = $active_sub;
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -212,8 +212,7 @@ class StaticContentController extends Controller
|
|||
throw $this->createNotFoundException('The item does not exist');
|
||||
|
||||
// set and save values
|
||||
$row->setID($req->request->get('id'))
|
||||
->setContent($req->request->get('content'));
|
||||
$row->setContent($req->request->get('content'));
|
||||
|
||||
// validate
|
||||
$errors = $validator->validate($row);
|
||||
|
|
@ -221,13 +220,6 @@ class StaticContentController extends Controller
|
|||
// initialize error list
|
||||
$error_array = [];
|
||||
|
||||
// check for duplicate ID
|
||||
$result = $em->getRepository(StaticContent::class)->find($id);
|
||||
if ($result != null)
|
||||
{
|
||||
$error_array['id'] = 'Duplicate ID exists.';
|
||||
}
|
||||
|
||||
// add errors to list
|
||||
foreach ($errors as $error) {
|
||||
$error_array[$error->getPropertyPath()] = $error->getMessage();
|
||||
|
|
|
|||
|
|
@ -57,6 +57,12 @@ class BatterySize
|
|||
*/
|
||||
protected $tip_lazada;
|
||||
|
||||
// subscription msrp
|
||||
/**
|
||||
* @ORM\Column(type="decimal", precision=7, scale=2, nullable=true)
|
||||
*/
|
||||
protected $sub_recurring_fee;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->batteries = new ArrayCollection();
|
||||
|
|
@ -64,6 +70,7 @@ class BatterySize
|
|||
$this->tip_premium = 0;
|
||||
$this->tip_other = 0;
|
||||
$this->tip_lazada = 0;
|
||||
$this->sub_recurring_fee = 0;
|
||||
}
|
||||
|
||||
public function getID()
|
||||
|
|
@ -149,4 +156,14 @@ class BatterySize
|
|||
return $this->tip_lazada;
|
||||
}
|
||||
|
||||
public function setSubRecurringFee($sub_recurring_fee)
|
||||
{
|
||||
$this->sub_recurring_fee = $sub_recurring_fee;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSubRecurringFee()
|
||||
{
|
||||
return $this->sub_recurring_fee;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Entity;
|
||||
|
||||
use App\Ramcar\InsuranceApplicationStatus;
|
||||
use App\Ramcar\SubscriptionStatus;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
|
|
@ -122,6 +123,12 @@ class CustomerVehicle
|
|||
*/
|
||||
protected $insurance_applications;
|
||||
|
||||
// link to subscription
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="Subscription", mappedBy="customer_vehicle")
|
||||
*/
|
||||
protected $subscriptions;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->flag_active = true;
|
||||
|
|
@ -306,4 +313,26 @@ class CustomerVehicle
|
|||
|
||||
return !empty($result) ? $result[0] : null;
|
||||
}
|
||||
|
||||
public function getSubscriptions()
|
||||
{
|
||||
return $this->subscriptions;
|
||||
}
|
||||
|
||||
public function getLatestSubscription()
|
||||
{
|
||||
// we get the latest subscription that actually started
|
||||
$criteria = Criteria::create()
|
||||
->where(Criteria::expr()->notIn('status', [
|
||||
SubscriptionStatus::CANCELLED,
|
||||
SubscriptionStatus::PENDING,
|
||||
]))
|
||||
->where(Criteria::expr()->neq('date_start', null))
|
||||
->orderBy(['date_create' => Criteria::DESC])
|
||||
->setMaxResults(1);
|
||||
|
||||
$result = $this->subscriptions->matching($criteria);
|
||||
|
||||
return !empty($result) ? $result[0] : null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
214
src/Entity/Subscription.php
Normal file
214
src/Entity/Subscription.php
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="subscription")
|
||||
*/
|
||||
class Subscription
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
// link to customer
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Customer")
|
||||
* @ORM\JoinColumn(name="customer_id", referencedColumnName="id")
|
||||
*/
|
||||
protected $customer;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="CustomerVehicle", inversedBy="subscriptions")
|
||||
* @ORM\JoinColumn(name="customer_vehicle_id", referencedColumnName="id")
|
||||
*/
|
||||
protected $customer_vehicle;
|
||||
|
||||
// email address
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
protected $email;
|
||||
|
||||
// date subscription was created
|
||||
/**
|
||||
* @ORM\Column(type="datetime")
|
||||
*/
|
||||
protected $date_create;
|
||||
|
||||
// date subscription starts
|
||||
/**
|
||||
* @ORM\Column(type="datetime", nullable=true)
|
||||
*/
|
||||
protected $date_start;
|
||||
|
||||
// date subscription ends
|
||||
/**
|
||||
* @ORM\Column(type="datetime", nullable=true)
|
||||
*/
|
||||
protected $date_end;
|
||||
|
||||
// date subscription was cancelled
|
||||
/**
|
||||
* @ORM\Column(type="datetime", nullable=true)
|
||||
*/
|
||||
protected $date_cancel;
|
||||
|
||||
// external api id (paymongo)
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
protected $ext_api_id;
|
||||
|
||||
// status of the subscription
|
||||
/**
|
||||
* @ORM\Column(type="string", length=50)
|
||||
* @Assert\NotBlank()
|
||||
*/
|
||||
protected $status;
|
||||
|
||||
// other data related to the transaction
|
||||
/**
|
||||
* @ORM\Column(type="json")
|
||||
*/
|
||||
protected $metadata;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->date_create = new DateTime();
|
||||
$this->date_start = null;
|
||||
$this->date_end = null;
|
||||
$this->date_cancel = null;
|
||||
$this->metadata = [];
|
||||
}
|
||||
|
||||
public function getID()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setCustomer(Customer $cust = null)
|
||||
{
|
||||
$this->customer = $cust;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCustomer()
|
||||
{
|
||||
return $this->customer;
|
||||
}
|
||||
|
||||
public function setCustomerVehicle(CustomerVehicle $customer_vehicle)
|
||||
{
|
||||
$this->customer_vehicle = $customer_vehicle;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCustomerVehicle()
|
||||
{
|
||||
return $this->customer_vehicle;
|
||||
}
|
||||
|
||||
public function setDateCreate(DateTime $date)
|
||||
{
|
||||
$this->date_create = $date;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDateCreate()
|
||||
{
|
||||
return $this->date_create;
|
||||
}
|
||||
|
||||
public function setDateStart(DateTime $date)
|
||||
{
|
||||
$this->date_start = $date;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDateStart()
|
||||
{
|
||||
return $this->date_start;
|
||||
}
|
||||
|
||||
public function setDateEnd(DateTime $date)
|
||||
{
|
||||
$this->date_end = $date;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDateEnd()
|
||||
{
|
||||
return $this->date_end;
|
||||
}
|
||||
|
||||
public function setDateCancel(DateTime $date)
|
||||
{
|
||||
$this->date_cancel = $date;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDateCancel()
|
||||
{
|
||||
return $this->date_cancel;
|
||||
}
|
||||
|
||||
public function setStatus($status)
|
||||
{
|
||||
$this->status = $status;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStatus()
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
public function setEmail($email)
|
||||
{
|
||||
$this->email = $email;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEmail()
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
public function setExtApiId($ext_api_id)
|
||||
{
|
||||
$this->ext_api_id = $ext_api_id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getExtApiId()
|
||||
{
|
||||
return $this->ext_api_id;
|
||||
}
|
||||
|
||||
public function setMetadata($metadata)
|
||||
{
|
||||
$this->metadata = $metadata;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMetadata()
|
||||
{
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
public function getGatewayTransactions()
|
||||
{
|
||||
// TODO: get gateway transactions here via type and metadata
|
||||
}
|
||||
}
|
||||
20
src/Ramcar/SubscriptionStatus.php
Normal file
20
src/Ramcar/SubscriptionStatus.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace App\Ramcar;
|
||||
|
||||
class SubscriptionStatus extends NameValue
|
||||
{
|
||||
const PENDING = 'pending';
|
||||
const ACTIVE = 'active';
|
||||
const ENDED = 'ended';
|
||||
const CANCELLED = 'cancelled';
|
||||
const REPOSSESSED = 'reposessed';
|
||||
|
||||
const COLLECTION = [
|
||||
'pending' => 'Pending',
|
||||
'active' => 'Active',
|
||||
'ended' => 'Ended',
|
||||
'cancelled' => 'Cancelled',
|
||||
'repossessed' => 'Reposessed',
|
||||
];
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ namespace App\Service\CustomerHandler;
|
|||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
|
@ -24,7 +25,7 @@ use App\Entity\Battery;
|
|||
use App\Entity\VehicleManufacturer;
|
||||
use App\Entity\BatteryManufacturer;
|
||||
use App\Entity\CustomerTag;
|
||||
|
||||
use App\Service\PayMongoConnector;
|
||||
use DateTime;
|
||||
|
||||
class ResqCustomerHandler implements CustomerHandlerInterface
|
||||
|
|
@ -34,14 +35,22 @@ class ResqCustomerHandler implements CustomerHandlerInterface
|
|||
protected $country_code;
|
||||
protected $security;
|
||||
protected $template_hash;
|
||||
protected $pm;
|
||||
|
||||
public function __construct(EntityManagerInterface $em, ValidatorInterface $validator,
|
||||
string $country_code, Security $security)
|
||||
string $country_code, Security $security, PayMongoConnector $pm, ParameterBagInterface $params)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->validator = $validator;
|
||||
$this->country_code = $country_code;
|
||||
$this->security = $security;
|
||||
$this->pm = $pm;
|
||||
|
||||
// initialize paymongo connector
|
||||
$this->pm->initialize(
|
||||
$params->get('subscription_paymongo_public_key'),
|
||||
$params->get('subscription_paymongo_secret_key'),
|
||||
);
|
||||
|
||||
$this->loadTemplates();
|
||||
}
|
||||
|
|
@ -391,6 +400,9 @@ class ResqCustomerHandler implements CustomerHandlerInterface
|
|||
$em->persist($cust);
|
||||
$em->flush();
|
||||
|
||||
// update customer paymongo record if exists
|
||||
$this->pm->updateCustomerIfExists($cust);
|
||||
|
||||
$result = [
|
||||
'id' => $cust->getID(),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -2,25 +2,39 @@
|
|||
|
||||
namespace App\Service;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\BatterySize;
|
||||
use Doctrine\ORM\Query\Parameter;
|
||||
|
||||
class PayMongoConnector
|
||||
{
|
||||
protected $base_url;
|
||||
protected $public_key;
|
||||
protected $secret_key;
|
||||
protected $hash;
|
||||
|
||||
public function __construct($base_url, $public_key, $secret_key)
|
||||
protected $public_hash;
|
||||
protected $secret_hash;
|
||||
|
||||
protected $sub_months;
|
||||
|
||||
public function __construct($base_url, ParameterBagInterface $params)
|
||||
{
|
||||
$this->base_url = $base_url;
|
||||
$this->sub_months = $params->get('subscription_months');
|
||||
}
|
||||
|
||||
public function initialize($public_key, $secret_key)
|
||||
{
|
||||
$this->public_key = $public_key;
|
||||
$this->secret_key = $secret_key;
|
||||
$this->hash = $this->generateHash();
|
||||
$this->public_hash = $this->generateHash($this->public_key);
|
||||
$this->secret_hash = $this->generateHash($this->secret_key);
|
||||
}
|
||||
|
||||
public function createCheckout(Customer $cust, $items, $ref_no = null, $description = null, $success_url = null, $cancel_url = null, $metadata = [])
|
||||
|
|
@ -84,25 +98,251 @@ class PayMongoConnector
|
|||
return $this->doRequest('/v1/webhooks/' . $id . '/enable', 'POST');
|
||||
}
|
||||
|
||||
protected function generateHash()
|
||||
public function getPlans()
|
||||
{
|
||||
return base64_encode($this->secret_key);
|
||||
return $this->doRequest('/v1/subscriptions/plans?limit=100', 'GET');
|
||||
}
|
||||
|
||||
protected function doRequest($url, $method, $request_body = [])
|
||||
public function getPlanByBatterySize(BatterySize $bsize)
|
||||
{
|
||||
$client = new Client();
|
||||
$headers = [
|
||||
'Content-Type' => 'application/json',
|
||||
'accept' => 'application/json',
|
||||
'authorization' => 'Basic '. $this->hash,
|
||||
// get all plans
|
||||
$plans = $this->getPlans();
|
||||
|
||||
// find the plan with the matching metadata for plan ID
|
||||
$found_plan = null;
|
||||
|
||||
if ($plans['success'] && !empty($plans['response']['data'])) {
|
||||
foreach ($plans['response']['data'] as $plan) {
|
||||
$plan_bsize_id = $plan['attributes']['metadata']['battery_size_id'] ?? null;
|
||||
|
||||
if ($plan_bsize_id == $bsize->getID()) {
|
||||
$found_plan = $plan;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $found_plan;
|
||||
}
|
||||
|
||||
public function createPlan($plan_data)
|
||||
{
|
||||
$body = [
|
||||
'data' => [
|
||||
'attributes' => [
|
||||
'amount' => $plan_data['amount'],
|
||||
'currency' => 'PHP',
|
||||
'description' => $plan_data['description'],
|
||||
'interval' => $plan_data['interval'],
|
||||
'interval_count' => $plan_data['interval_count'],
|
||||
//'cycle_count' => $plan_data['cycle_count'],
|
||||
'name' => $plan_data['name'],
|
||||
'metadata' => $plan_data['metadata'],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
try {
|
||||
$response = $client->request($method, $this->base_url . '/' . $url, [
|
||||
'json' => $request_body,
|
||||
'headers' => $headers,
|
||||
return $this->doRequest('/v1/subscriptions/plans', 'POST', $body);
|
||||
}
|
||||
|
||||
public function updatePlan($plan_id, $plan_data)
|
||||
{
|
||||
$body = [
|
||||
'data' => [
|
||||
'attributes' => [
|
||||
'amount' => $plan_data['amount'],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $this->doRequest('/v1/subscriptions/plans/' . $plan_id, 'PUT', $body);
|
||||
}
|
||||
|
||||
public function createOrUpdateSubPlan(BatterySize $bsize)
|
||||
{
|
||||
$found_plan = $this->getPlanByBatterySize($bsize);
|
||||
|
||||
if (!empty($found_plan)) {
|
||||
// update existing plan
|
||||
$result = $this->updatePlan($found_plan['id'], [
|
||||
'amount' => (int)bcmul($bsize->getSubRecurringFee(), 100),
|
||||
]);
|
||||
} else {
|
||||
// create new plan
|
||||
$result = $this->createPlan([
|
||||
'name' => "RESQ Subscription",
|
||||
'amount' => (int)bcmul($bsize->getSubRecurringFee() ?? 0, 100),
|
||||
'description' => "Motolite Battery Subscription Plan",
|
||||
'interval' => 'monthly',
|
||||
'interval_count' => 1,
|
||||
//'cycle_count' => $this->sub_months,
|
||||
'metadata' => [
|
||||
'battery_size_id' => (string)$bsize->getID(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function findCustomerByEmail($email)
|
||||
{
|
||||
return $this->doRequest('/v1/customers?email=' . $email, 'GET');
|
||||
}
|
||||
|
||||
public function createCustomer(Customer $cust, $email_override = "")
|
||||
{
|
||||
$body = [
|
||||
'data' => [
|
||||
'attributes' => [
|
||||
'first_name' => $cust->getFirstName(),
|
||||
'last_name' => $cust->getLastName(),
|
||||
'phone' => "+63" . $cust->getPhoneMobile(),
|
||||
'email' => $email_override ?? $cust->getEmail(),
|
||||
'default_device' => 'email',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $this->doRequest('/v1/customers', 'POST', $body);
|
||||
}
|
||||
|
||||
public function updateCustomer(Customer $cust, $ext_cust_id)
|
||||
{
|
||||
$body = [
|
||||
'data' => [
|
||||
'attributes' => [
|
||||
'first_name' => $cust->getFirstName(),
|
||||
'last_name' => $cust->getLastName(),
|
||||
'phone' => $cust->getPhoneMobile(),
|
||||
'email' => $cust->getEmail(),
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $this->doRequest('/v1/customers/' . $ext_cust_id, 'PUT', $body);
|
||||
}
|
||||
|
||||
public function updateCustomerIfExists(Customer $cust)
|
||||
{
|
||||
$email = $cust->getEmail();
|
||||
|
||||
// if no email, then we don't need to update
|
||||
if (empty($email)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if we have an existing paymongo customer with this email
|
||||
$found_cust = $this->findCustomerByEmail($email);
|
||||
|
||||
if (isset($found_cust['data']['id'])) {
|
||||
// update existing customer record
|
||||
return $this->updateCustomer($cust, $found_cust['data']);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function findOrCreateCustomer($email, Customer $cust)
|
||||
{
|
||||
error_log("FINDING RECORD FOR $email");
|
||||
|
||||
// check if we have an existing paymongo customer with this email
|
||||
$found_cust = $this->findCustomerByEmail($email);
|
||||
|
||||
$pm_cust = null;
|
||||
|
||||
error_log("FOUND CUSTOMER?");
|
||||
error_log(print_r($found_cust, true));
|
||||
|
||||
if (isset($found_cust['response']['data'][0]['id'])) {
|
||||
// we found a customer record
|
||||
$pm_cust = $found_cust['response']['data'][0];
|
||||
} else {
|
||||
error_log("CREATING CUSTOMER");
|
||||
|
||||
// we create a new customer record
|
||||
$new_cust = $this->createCustomer($cust, $email);
|
||||
|
||||
error_log("NEW CUST RESPONSE");
|
||||
error_log(print_r($new_cust, true));
|
||||
|
||||
if (isset($new_cust['response']['data']['id'])) {
|
||||
// customer record was created successfully
|
||||
$pm_cust = $new_cust['response']['data'];
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: if $pm_cust is null at this point, an error occurred during customer creation and we check the request logs for more details
|
||||
return $pm_cust;
|
||||
}
|
||||
|
||||
public function createSubscription($ext_cust_id, $plan_id)
|
||||
{
|
||||
$body = [
|
||||
'data' => [
|
||||
'attributes' => [
|
||||
'customer_id' => $ext_cust_id,
|
||||
'plan_id' => $plan_id,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $this->doRequest('/v1/subscriptions', 'POST', $body);
|
||||
}
|
||||
|
||||
public function getSubscription($sub_id)
|
||||
{
|
||||
return $this->doRequest('/v1/subscriptions/'. $sub_id, 'GET');
|
||||
}
|
||||
|
||||
public function getPaymentIntent($pi_id)
|
||||
{
|
||||
return $this->doRequest('/v1/payment_intents/' . $pi_id, 'GET');
|
||||
}
|
||||
|
||||
public function attachPaymentIntent($pm_id, $pi_id)
|
||||
{
|
||||
$body = [
|
||||
'data' => [
|
||||
'attributes' => [
|
||||
'payment_method' => $pm_id,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $this->doRequest('/v1/payment_intents/' . $pi_id . '/attach', 'POST', $body);
|
||||
}
|
||||
|
||||
protected function generateHash($key)
|
||||
{
|
||||
return base64_encode($key);
|
||||
}
|
||||
|
||||
protected function buildHeaders($use_public_key = false)
|
||||
{
|
||||
$hash = $use_public_key ? $this->public_hash : $this->secret_hash;
|
||||
|
||||
return [
|
||||
'Content-Type' => 'application/json',
|
||||
'accept' => 'application/json',
|
||||
'authorization' => 'Basic '. $hash,
|
||||
];
|
||||
}
|
||||
|
||||
protected function doRequest($url, $method, $request_body = [], $use_public_key = false)
|
||||
{
|
||||
$client = new Client();
|
||||
$headers = $this->buildHeaders($use_public_key);
|
||||
|
||||
$request_params = ['headers' => $headers];
|
||||
|
||||
if (!empty($request_body)) {
|
||||
$request_params['json'] = $request_body;
|
||||
}
|
||||
|
||||
try {
|
||||
$response = $client->request($method, $this->base_url . $url, $request_params);
|
||||
} catch (RequestException $e) {
|
||||
$error = ['message' => $e->getMessage()];
|
||||
|
||||
|
|
|
|||
|
|
@ -71,15 +71,24 @@
|
|||
<div class="form-control-feedback hide" data-field="tip_other"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group m-form__group row no-border">
|
||||
<label class="col-lg-3 col-form-label" data-field="tip_lazada">
|
||||
{% trans %}battery_size_tradein_lazada{% endtrans %}
|
||||
</label>
|
||||
<div class="col-lg-9">
|
||||
<input type="text" name="tip_lazada" class="form-control m-input" value="{{ obj.getTIPriceLazada }}">
|
||||
<div class="form-control-feedback hide" data-field="tip_lazada"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group m-form__group row no-border">
|
||||
<label class="col-lg-3 col-form-label" data-field="tip_lazada">
|
||||
{% trans %}battery_size_tradein_lazada{% endtrans %}
|
||||
</label>
|
||||
<div class="col-lg-9">
|
||||
<input type="text" name="tip_lazada" class="form-control m-input" value="{{ obj.getTIPriceLazada }}">
|
||||
<div class="form-control-feedback hide" data-field="tip_lazada"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group m-form__group row no-border">
|
||||
<label class="col-lg-3 col-form-label" data-field="sub_recurring_fee">
|
||||
{% trans %}battery_Size_sub_recurring_fee{% endtrans %}
|
||||
</label>
|
||||
<div class="col-lg-9">
|
||||
<input type="text" name="sub_recurring_fee" class="form-control m-input" value="{{ obj.getSubRecurringFee }}">
|
||||
<div class="form-control-feedback hide" data-field="sub_recurring_fee"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-portlet__foot m-portlet__foot--fit">
|
||||
<div class="m-form__actions m-form__actions--solid m-form__actions--right">
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
<label data-field="id">
|
||||
ID:
|
||||
</label>
|
||||
<input type="text" name="id" class="form-control m-input" value="{{ obj.getID() }}">
|
||||
<input type="text" name="id" class="form-control m-input" value="{{ obj.getID() }}" {{ mode == 'update' ? 'disabled' }}>
|
||||
<div class="form-control-feedback hide" data-field="id"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -48,7 +48,7 @@
|
|||
<label data-field="content">
|
||||
Content
|
||||
</label>
|
||||
<textarea name="content" class="form-control m-input" data-name="content" rows="50">{{ obj.getContent() }}</textarea>
|
||||
<textarea id="content" name="content" class="form-control m-input" data-name="content" rows="50">{{ obj.getContent() }}</textarea>
|
||||
<div class="form-control-feedback hide" data-field="content"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -70,8 +70,19 @@
|
|||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block stylesheets %}
|
||||
<link rel="stylesheet" href="https://unpkg.com/easymde/dist/easymde.min.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="https://unpkg.com/easymde/dist/easymde.min.js"></script>
|
||||
<script>
|
||||
// load markdown editor
|
||||
const mde = new EasyMDE({
|
||||
element: document.getElementById('content'),
|
||||
spellChecker: false,
|
||||
});
|
||||
|
||||
$(function() {
|
||||
$("#row-form").submit(function(e) {
|
||||
var form = $(this);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ battery_size_tradein_brand: Trade-in Motolite
|
|||
battery_size_tradein_premium: Trade-in Premium
|
||||
battery_size_tradein_other: Trade-in Other
|
||||
battery_size_tradein_lazada: Trade-in Lazada
|
||||
battery_Size_sub_recurring_fee: Subscription Recurring Fee
|
||||
add_cust_vehicle_battery_info: This vehicle is using a Motolite battery
|
||||
jo_title_pdf: Motolite Res-Q Job Order
|
||||
country_code_prefix: '+63'
|
||||
|
|
|
|||
Loading…
Reference in a new issue