Add source to invoice criteria. Modify invoice rules to get service fees from... #1701
24 changed files with 1667 additions and 107 deletions
|
|
@ -53,6 +53,14 @@ security:
|
|||
pattern: ^\/test_capi\/
|
||||
security: false
|
||||
|
||||
insurance:
|
||||
pattern: ^\/insurance\/
|
||||
security: false
|
||||
|
||||
paymongo:
|
||||
pattern: ^\/paymongo\/
|
||||
security: false
|
||||
|
||||
cust_api_v2:
|
||||
pattern: ^\/apiv2\/(?!register|register\/|number_confirm|number_confirm\/|code_validate|code_validate\/|resend_code|resend_code\/|version_check|version_check\/|account|account\/|account_code_validate|account_code_validate\/|account_resend_code|account_resend_code\/)
|
||||
provider: api_v2_provider
|
||||
|
|
|
|||
|
|
@ -45,6 +45,11 @@ apiv2_cust_vehicle_add:
|
|||
controller: App\Controller\CustomerAppAPI\VehicleController::addVehicle
|
||||
methods: [POST]
|
||||
|
||||
apiv2_cust_vehicle_info:
|
||||
path: /apiv2/vehicles/{id}
|
||||
controller: App\Controller\CustomerAppAPI\VehicleController::getVehicle
|
||||
methods: [GET]
|
||||
|
||||
apiv2_cust_vehicle_update:
|
||||
path: /apiv2/vehicles/{id}
|
||||
controller: App\Controller\CustomerAppAPI\VehicleController::updateVehicle
|
||||
|
|
@ -260,4 +265,35 @@ apiv2_account_delete_resend_code:
|
|||
apiv2_account_delete_code_validate:
|
||||
path: /apiv2/account_delete_code_validate
|
||||
controller: App\Controller\CustomerAppAPI\AccountController::validateDeleteCode
|
||||
methods: [POST]
|
||||
|
||||
# insurance
|
||||
apiv2_insurance_vehicle_maker_list:
|
||||
path: /apiv2/insurance/vehicles/makers
|
||||
controller: App\Controller\CustomerAppAPI\InsuranceController::getVehicleMakers
|
||||
methods: [GET]
|
||||
|
||||
apiv2_insurance_vehicle_model_list:
|
||||
path: /apiv2/insurance/vehicles/models/{maker_id}
|
||||
controller: App\Controller\CustomerAppAPI\InsuranceController::getVehicleModels
|
||||
methods: [GET]
|
||||
|
||||
apiv2_insurance_vehicle_trim_list:
|
||||
path: /apiv2/insurance/vehicles/trims/{model_id}
|
||||
controller: App\Controller\CustomerAppAPI\InsuranceController::getVehicleTrims
|
||||
methods: [GET]
|
||||
|
||||
apiv2_insurance_vehicle_mv_type_list:
|
||||
path: /apiv2/insurance/mvtypes
|
||||
controller: App\Controller\CustomerAppAPI\InsuranceController::getMVTypes
|
||||
methods: [GET]
|
||||
|
||||
apiv2_insurance_vehicle_client_type_list:
|
||||
path: /apiv2/insurance/clienttypes
|
||||
controller: App\Controller\CustomerAppAPI\InsuranceController::getClientTypes
|
||||
methods: [GET]
|
||||
|
||||
apiv2_insurance_application_create:
|
||||
path: /apiv2/insurance/application
|
||||
controller: App\Controller\CustomerAppAPI\InsuranceController::createApplication
|
||||
methods: [POST]
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# insurance
|
||||
|
||||
insurance_listener:
|
||||
path: /api/insurance/listen
|
||||
path: /insurance/listen
|
||||
controller: App\Controller\InsuranceController::listen
|
||||
methods: [POST]
|
||||
|
|
|
|||
16
config/routes/paymongo.yaml
Normal file
16
config/routes/paymongo.yaml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# paymongo
|
||||
|
||||
paymongo_listener:
|
||||
path: /paymongo/listen
|
||||
controller: App\Controller\PayMongoController::listen
|
||||
methods: [POST]
|
||||
|
||||
paymongo_payment_success:
|
||||
path: /paymongo/success
|
||||
controller: App\Controller\PayMongoController::paymentSuccess
|
||||
methods: [GET]
|
||||
|
||||
paymongo_payment_cancelled:
|
||||
path: /paymongo/cancelled
|
||||
controller: App\Controller\PayMongoController::paymentCancelled
|
||||
methods: [GET]
|
||||
|
|
@ -216,6 +216,23 @@ services:
|
|||
$username: "%env(INSURANCE_USERNAME)%"
|
||||
$password: "%env(INSURANCE_PASSWORD)%"
|
||||
|
||||
# entity listener for gateway transactions
|
||||
App\EntityListener\GatewayTransactionListener:
|
||||
arguments:
|
||||
$em: "@doctrine.orm.entity_manager"
|
||||
$ic: "@App\\Service\\InsuranceConnector"
|
||||
tags:
|
||||
- name: doctrine.orm.entity_listener
|
||||
event: 'postUpdate'
|
||||
entity: 'App\Entity\GatewayTransaction'
|
||||
|
||||
# paymongo connector
|
||||
App\Service\PayMongoConnector:
|
||||
arguments:
|
||||
$base_url: "%env(PAYMONGO_BASE_URL)%"
|
||||
$public_key: "%env(PAYMONGO_PUBLIC_KEY)%"
|
||||
$secret_key: "%env(PAYMONGO_SECRET_KEY)%"
|
||||
|
||||
# entity listener for customer vehicle warranty code history
|
||||
App\EntityListener\CustomerVehicleSerialListener:
|
||||
arguments:
|
||||
|
|
|
|||
324
src/Controller/CustomerAppAPI/InsuranceController.php
Normal file
324
src/Controller/CustomerAppAPI/InsuranceController.php
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller\CustomerAppAPI;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Catalyst\ApiBundle\Component\Response as ApiResponse;
|
||||
|
||||
use App\Service\InsuranceConnector;
|
||||
use App\Service\PayMongoConnector;
|
||||
|
||||
use App\Entity\InsuranceApplication;
|
||||
use App\Entity\GatewayTransaction;
|
||||
use App\Entity\CustomerVehicle;
|
||||
|
||||
use App\Ramcar\InsuranceApplicationStatus;
|
||||
use App\Ramcar\InsuranceMVType;
|
||||
use App\Ramcar\InsuranceClientType;
|
||||
use App\Ramcar\TransactionStatus;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
use DateTime;
|
||||
|
||||
class InsuranceController extends ApiController
|
||||
{
|
||||
protected $client;
|
||||
|
||||
public function __construct(EntityManagerInterface $em, KernelInterface $kernel, InsuranceConnector $client)
|
||||
{
|
||||
parent::__construct($em, $kernel);
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
public function createApplication(Request $req, PayMongoConnector $paymongo, UrlGeneratorInterface $router)
|
||||
{
|
||||
// validate params
|
||||
$validity = $this->validateRequest($req, [
|
||||
// internal
|
||||
'customer_vehicle_id',
|
||||
|
||||
// client info
|
||||
'client_type',
|
||||
'first_name',
|
||||
//'middle_name', // not required
|
||||
'surname',
|
||||
'corporate_name',
|
||||
|
||||
// client contact info
|
||||
'address_number',
|
||||
//'address_street', // not required
|
||||
//'address_building', // not required
|
||||
'address_barangay',
|
||||
'address_city',
|
||||
'address_province',
|
||||
'zipcode',
|
||||
'mobile_number',
|
||||
'email_address',
|
||||
|
||||
// car info
|
||||
'make',
|
||||
'model',
|
||||
'series',
|
||||
'color',
|
||||
//'plate_number', // NOTE: we get this from the internal cv record instead
|
||||
'mv_file_number',
|
||||
'motor_number',
|
||||
'serial_chasis', // NOTE: this is how it's spelled on their API
|
||||
'year_model',
|
||||
'mv_type_id',
|
||||
'body_type',
|
||||
//'is_public', // not required, boolean, only show field if mv_type_id in [4, 13]
|
||||
//'orcr_file', // this is a file
|
||||
|
||||
// mv_type_id specific fields
|
||||
//'vehicle_use_type', // not required, only show field if mv_type_id is not in [4, 13]. accepted values are: 'commercial', 'private'
|
||||
]);
|
||||
|
||||
if (!$validity['is_valid']) {
|
||||
return new ApiResponse(false, $validity['error']);
|
||||
}
|
||||
|
||||
// conditionally require is_public or vehicle_use_type
|
||||
switch ($req->request->get('mv_type_id')) {
|
||||
case 4:
|
||||
case 13:
|
||||
if (empty($req->request->get('is_public'))) {
|
||||
return new ApiResponse(false, 'Missing required parameter(s): is_public is required when mv_type_id is in [4, 13]');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (empty($req->request->get('vehicle_use_type'))) {
|
||||
return new ApiResponse(false, 'Missing required parameter(s): vehicle_use_type is required when mv_type_id is not in [4, 13]');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// require the orcr file
|
||||
if ($req->files->get('orcr_file') === null) {
|
||||
return new ApiResponse(false, 'Missing required file: orcr_file');
|
||||
}
|
||||
|
||||
// get our listener url
|
||||
$notif_url = $router->generate('insurance_listener', [], UrlGeneratorInterface::ABSOLUTE_URL);
|
||||
|
||||
// get customer and cv info
|
||||
$cust = $this->session->getCustomer();
|
||||
$cv = $this->em->getRepository(CustomerVehicle::class)->find($req->request->get('customer_vehicle_id'));
|
||||
if ($cv == null) {
|
||||
return new ApiResponse(false, 'Invalid customer vehicle id.');
|
||||
}
|
||||
|
||||
// confirm that customer vehicle belongs to customer
|
||||
if ($cv->getCustomer()->getID() != $cust->getID()) {
|
||||
return new ApiResponse(false, 'Vehicle does not belong to customer.');
|
||||
}
|
||||
|
||||
// process all our inputs first
|
||||
$input = $req->request->all();
|
||||
|
||||
if (!isset($input['is_public'])) {
|
||||
$input['is_public'] = false;
|
||||
}
|
||||
|
||||
$input['line'] = $this->getLineType($input['mv_type_id'], $input['vehicle_use_type'], $input['is_public']);
|
||||
|
||||
// submit insurance application
|
||||
$result = $this->client->createApplication(
|
||||
$cv,
|
||||
$notif_url,
|
||||
$input,
|
||||
$req->files->get('orcr_file')
|
||||
);
|
||||
if (!$result['success']) {
|
||||
return new ApiResponse(false, $result['error']['message']);
|
||||
}
|
||||
|
||||
$premium_amount_int = (int)bcmul($result['response']['premium'], 100);
|
||||
|
||||
// build checkout item and metadata
|
||||
$items = [
|
||||
[
|
||||
'name' => "Insurance Premium",
|
||||
'description' => "Premium fee for vehicle insurance",
|
||||
'quantity' => 1,
|
||||
'amount' => $premium_amount_int,
|
||||
'currency' => 'PHP',
|
||||
],
|
||||
];
|
||||
|
||||
$now = new DateTime();
|
||||
|
||||
// create gateway transaction
|
||||
$gt = new GatewayTransaction();
|
||||
$gt->setCustomer($cust);
|
||||
$gt->setDateCreate($now);
|
||||
$gt->setAmount($premium_amount_int);
|
||||
$gt->setStatus(TransactionStatus::PENDING);
|
||||
$gt->setGateway('paymongo'); // TODO: define values elsewhere
|
||||
$gt->setType('insurance_premium'); // TODO: define values elsewhere
|
||||
$this->em->persist($gt);
|
||||
$this->em->flush();
|
||||
|
||||
// create paymongo checkout resource
|
||||
$checkout = $paymongo->createCheckout(
|
||||
$cust,
|
||||
$items,
|
||||
$gt->getID(),
|
||||
"Motolite RES-Q Vehicle Insurance",
|
||||
$router->generate('paymongo_payment_success', [], UrlGeneratorInterface::ABSOLUTE_URL),
|
||||
$router->generate('paymongo_payment_cancelled', [], UrlGeneratorInterface::ABSOLUTE_URL),
|
||||
['transaction_id' => $gt->getID()], // NOTE: passing this here too for payment resource metadata
|
||||
);
|
||||
if (!$checkout['success']) {
|
||||
return new ApiResponse(false, $checkout['error']['message']);
|
||||
}
|
||||
|
||||
$checkout_url = $checkout['response']['data']['attributes']['checkout_url'];
|
||||
|
||||
// add checkout url and id to transaction metadata
|
||||
$gt->setExtTransactionId($checkout['response']['data']['id']);
|
||||
$gt->setMetadata([
|
||||
'checkout_url' => $checkout_url,
|
||||
]);
|
||||
|
||||
// store application in db
|
||||
$app = new InsuranceApplication();
|
||||
$app->setDateSubmit($now);
|
||||
$app->setCustomer($cust);
|
||||
$app->setCustomerVehicle($cv);
|
||||
$app->setGatewayTransaction($gt);
|
||||
$app->setStatus(InsuranceApplicationStatus::CREATED);
|
||||
$app->setExtTransactionId($result['response']['id']);
|
||||
$app->setMetadata($input);
|
||||
$this->em->persist($app);
|
||||
|
||||
// save everything
|
||||
$this->em->flush();
|
||||
|
||||
// return
|
||||
return new ApiResponse(true, '', [
|
||||
'app_id' => $app->getID(),
|
||||
'checkout_url' => $checkout_url,
|
||||
'premium_amount' => (string)$result['response']['premium'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function getVehicleMakers(Request $req)
|
||||
{
|
||||
// validate params
|
||||
$validity = $this->validateRequest($req);
|
||||
|
||||
if (!$validity['is_valid']) {
|
||||
return new ApiResponse(false, $validity['error']);
|
||||
}
|
||||
|
||||
// get maker list
|
||||
$result = $this->client->getVehicleMakers();
|
||||
if (!$result['success']) {
|
||||
return new ApiResponse(false, $result['error']['message']);
|
||||
}
|
||||
|
||||
return new ApiResponse(true, '', [
|
||||
'makers' => $result['response']['data']['vehicleMakers'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function getVehicleModels($maker_id, Request $req)
|
||||
{
|
||||
// validate params
|
||||
$validity = $this->validateRequest($req);
|
||||
|
||||
if (!$validity['is_valid']) {
|
||||
return new ApiResponse(false, $validity['error']);
|
||||
}
|
||||
|
||||
// get maker list
|
||||
$result = $this->client->getVehicleModels($maker_id);
|
||||
if (!$result['success']) {
|
||||
return new ApiResponse(false, $result['error']['message']);
|
||||
}
|
||||
|
||||
return new ApiResponse(true, '', [
|
||||
'models' => $result['response']['data']['vehicleModels'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function getVehicleTrims($model_id, Request $req)
|
||||
{
|
||||
// validate params
|
||||
$validity = $this->validateRequest($req);
|
||||
|
||||
if (!$validity['is_valid']) {
|
||||
return new ApiResponse(false, $validity['error']);
|
||||
}
|
||||
|
||||
// get maker list
|
||||
$result = $this->client->getVehicleTrims($model_id);
|
||||
if (!$result['success']) {
|
||||
return new ApiResponse(false, $result['error']['message']);
|
||||
}
|
||||
|
||||
return new ApiResponse(true, '', [
|
||||
'trims' => $result['response']['data']['vehicleTrims'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function getMVTypes(Request $req)
|
||||
{
|
||||
// validate params
|
||||
$validity = $this->validateRequest($req);
|
||||
|
||||
if (!$validity['is_valid']) {
|
||||
return new ApiResponse(false, $validity['error']);
|
||||
}
|
||||
|
||||
return new ApiResponse(true, '', [
|
||||
'mv_types' => InsuranceMVType::getCollection(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getClientTypes(Request $req)
|
||||
{
|
||||
// validate params
|
||||
$validity = $this->validateRequest($req);
|
||||
|
||||
if (!$validity['is_valid']) {
|
||||
return new ApiResponse(false, $validity['error']);
|
||||
}
|
||||
|
||||
return new ApiResponse(true, '', [
|
||||
'mv_types' => InsuranceClientType::getCollection(),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getLineType($mv_type_id, $vehicle_use_type, $is_public = false)
|
||||
{
|
||||
$line = '';
|
||||
|
||||
// NOTE: this is a bit of a hack since we're hardcoding values, but this is fine for now
|
||||
switch ($mv_type_id) {
|
||||
case '3':
|
||||
$line = 'mcoc';
|
||||
break;
|
||||
case '4':
|
||||
case '13':
|
||||
if ($is_public) {
|
||||
$line = 'lcoc';
|
||||
} else {
|
||||
$line = 'mcoc';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ($vehicle_use_type === 'commercial') {
|
||||
$line = 'ccoc';
|
||||
} else {
|
||||
$line = 'pcoc';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $line;
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,8 @@ use Catalyst\ApiBundle\Component\Response as ApiResponse;
|
|||
use App\Entity\CustomerVehicle;
|
||||
use App\Entity\VehicleManufacturer;
|
||||
use App\Entity\Vehicle;
|
||||
|
||||
use App\Ramcar\InsuranceApplicationStatus;
|
||||
use App\Service\PayMongoConnector;
|
||||
use DateTime;
|
||||
|
||||
class VehicleController extends ApiController
|
||||
|
|
@ -107,6 +108,34 @@ class VehicleController extends ApiController
|
|||
|
||||
}
|
||||
|
||||
public function getVehicle(Request $req, $id, PayMongoConnector $paymongo)
|
||||
{
|
||||
// check requirements
|
||||
$validity = $this->validateRequest($req);
|
||||
|
||||
if (!$validity['is_valid']) {
|
||||
return new ApiResponse(false, $validity['error']);
|
||||
}
|
||||
|
||||
// get customer vehicle
|
||||
$cv = $this->em->getRepository(CustomerVehicle::class)->find($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() != $this->session->getCustomer()->getID()) {
|
||||
return new ApiResponse(false, 'Invalid vehicle.');
|
||||
}
|
||||
|
||||
// response
|
||||
return new ApiResponse(true, '', [
|
||||
'vehicle' => $this->generateVehicleInfo($cv, true, $paymongo),
|
||||
]);
|
||||
}
|
||||
|
||||
public function updateVehicle(Request $req, $id)
|
||||
{
|
||||
// check requirements
|
||||
|
|
@ -141,7 +170,7 @@ class VehicleController extends ApiController
|
|||
]);
|
||||
}
|
||||
|
||||
public function listVehicles(Request $req)
|
||||
public function listVehicles(Request $req, PayMongoConnector $paymongo)
|
||||
{
|
||||
// validate params
|
||||
$validity = $this->validateRequest($req);
|
||||
|
|
@ -162,37 +191,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) {
|
||||
$battery_id = null;
|
||||
if ($cv->getCurrentBattery() != null)
|
||||
$battery_id = $cv->getCurrentBattery()->getID();
|
||||
|
||||
$wty_ex = null;
|
||||
if ($cv->getWarrantyExpiration() != null)
|
||||
$wty_ex = $cv->getWarrantyExpiration()->format('Y-m-d');
|
||||
|
||||
$warranty = $this->findWarranty($cv->getPlateNumber());
|
||||
|
||||
$cv_name = '';
|
||||
if ($cv->getName() != null)
|
||||
$cv_name = $cv->getName();
|
||||
|
||||
$cv_list[] = [
|
||||
'cv_id' => $cv->getID(),
|
||||
'mfg_id' => $cv->getVehicle()->getManufacturer()->getID(),
|
||||
'make_id' => $cv->getVehicle()->getID(),
|
||||
'name' => $cv_name,
|
||||
'plate_num' => $cv->getPlateNumber(),
|
||||
'model_year' => $cv->getModelYear(),
|
||||
'color' => $cv->getColor(),
|
||||
'condition' => $cv->getStatusCondition(),
|
||||
'fuel_type' => $cv->getFuelType(),
|
||||
'wty_code' => $cv->getWarrantyCode(),
|
||||
'wty_expire' => $wty_ex,
|
||||
'curr_batt_id' => $battery_id,
|
||||
'is_motolite' => $cv->hasMotoliteBattery() ? 1 : 0,
|
||||
'is_active' => $cv->isActive() ? 1 : 0,
|
||||
'warranty' => $warranty,
|
||||
];
|
||||
$cv_list[] = $this->generateVehicleInfo($cv, true, $paymongo);
|
||||
}
|
||||
|
||||
// response
|
||||
|
|
@ -285,6 +284,98 @@ class VehicleController extends ApiController
|
|||
return new ApiResponse();
|
||||
}
|
||||
|
||||
protected function generateVehicleInfo(CustomerVehicle $cv, $include_insurance = false, PayMongoConnector $paymongo)
|
||||
{
|
||||
$battery_id = null;
|
||||
if ($cv->getCurrentBattery() != null)
|
||||
$battery_id = $cv->getCurrentBattery()->getID();
|
||||
|
||||
$wty_ex = null;
|
||||
if ($cv->getWarrantyExpiration() != null)
|
||||
$wty_ex = $cv->getWarrantyExpiration()->format('Y-m-d');
|
||||
|
||||
$warranty = $this->findWarranty($cv->getPlateNumber());
|
||||
|
||||
$cv_name = '';
|
||||
if ($cv->getName() != null)
|
||||
$cv_name = $cv->getName();
|
||||
|
||||
$row = [
|
||||
'cv_id' => $cv->getID(),
|
||||
'mfg_id' => $cv->getVehicle()->getManufacturer()->getID(),
|
||||
'make_id' => $cv->getVehicle()->getID(),
|
||||
'name' => $cv_name,
|
||||
'plate_num' => $cv->getPlateNumber(),
|
||||
'model_year' => $cv->getModelYear(),
|
||||
'color' => $cv->getColor(),
|
||||
'condition' => $cv->getStatusCondition(),
|
||||
'fuel_type' => $cv->getFuelType(),
|
||||
'wty_code' => $cv->getWarrantyCode(),
|
||||
'wty_expire' => $wty_ex,
|
||||
'curr_batt_id' => $battery_id,
|
||||
'is_motolite' => $cv->hasMotoliteBattery() ? 1 : 0,
|
||||
'is_active' => $cv->isActive() ? 1 : 0,
|
||||
'warranty' => $warranty,
|
||||
];
|
||||
|
||||
// get latest insurance row
|
||||
if ($include_insurance) {
|
||||
$insurance = null;
|
||||
$iobj = $cv->getLatestInsuranceApplication();
|
||||
if (!empty($iobj)) {
|
||||
$gt = $iobj->getGatewayTransaction();
|
||||
$date_complete = $iobj->getDateComplete();
|
||||
$date_expire = $iobj->getDateExpire();
|
||||
$status = $iobj->getStatus();
|
||||
|
||||
error_log("\r\nTHIS IS THE CURRENT STATUS: " . $status . "\r\n");
|
||||
|
||||
// handle the very transient state between a payment being made and receiving the paymongo webhook
|
||||
// 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:
|
||||
// get latest status on this checkout from paymongo
|
||||
$checkout = $paymongo->getCheckout($gt->getExtTransactionId());
|
||||
|
||||
if ($checkout['success']) {
|
||||
$payment_intent = $checkout['response']['data']['attributes']['payment_intent'] ?? null;
|
||||
if (!empty($payment_intent)) {
|
||||
$intent_status = $payment_intent['attributes']['status'] ?? null;
|
||||
|
||||
// TODO: define these paymongo payment intent statuses elsewhere
|
||||
if ($intent_status === 'processing' || $intent_status === 'succeeded') {
|
||||
$status = InsuranceApplicationStatus::PAID;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
$insurance = [
|
||||
'id' => $iobj->getID(),
|
||||
'ext_transaction_id' => $iobj->getExtTransactionId(),
|
||||
'status' => $status,
|
||||
'coc_url' => $iobj->getCOC(),
|
||||
'checkout_url' => $gt->getMetadata()['checkout_url'],
|
||||
'transaction_status' => $gt->getStatus(),
|
||||
'premium_amount' => (string)bcdiv($gt->getAmount(), 100), // NOTE: hard expressing as string so it's consistent
|
||||
'date_submit' => $iobj->getDateSubmit()->format('Y-m-d H:i:s'),
|
||||
'date_complete' => $date_complete ? $date_complete->format('Y-m-d H:i:s') : null,
|
||||
'date_expire' => $date_expire ? $date_expire->format('Y-m-d H:i:s') : null,
|
||||
];
|
||||
|
||||
// get information changelog
|
||||
$insurance['changelog'] = $iobj->getMetadata()['changes'] ?? [];
|
||||
}
|
||||
|
||||
$row['latest_insurance'] = $insurance;
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
protected function checkVehicleRequirements(Request $req)
|
||||
{
|
||||
// validate params
|
||||
|
|
|
|||
|
|
@ -2,22 +2,134 @@
|
|||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Ramcar\InsuranceApplicationStatus;
|
||||
use App\Service\FCMSender;
|
||||
use App\Entity\InsuranceApplication;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
|
||||
use DateTime;
|
||||
|
||||
class InsuranceController extends Controller
|
||||
{
|
||||
public function listen(Request $req, EntityManagerInterface $em)
|
||||
protected $em;
|
||||
protected $fcmclient;
|
||||
|
||||
public function __construct(EntityManagerInterface $em, FCMSender $fcmclient)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->fcmclient = $fcmclient;
|
||||
}
|
||||
|
||||
public function listen(Request $req)
|
||||
{
|
||||
$payload = $req->request->all();
|
||||
|
||||
// DEBUG
|
||||
@file_put_contents(__DIR__ . '/../../var/log/insurance.log', print_r($payload, true) . "\r\n----------------------------------------\r\n\r\n", FILE_APPEND);
|
||||
error_log(print_r($payload, true));
|
||||
|
||||
/*
|
||||
return $this->json([
|
||||
'success' => true,
|
||||
]);
|
||||
*/
|
||||
|
||||
// END DEBUG
|
||||
|
||||
// if no transaction code given, silently fail
|
||||
if (empty($payload['transaction_code'])) {
|
||||
error_log("Invalid insurance callback received: " . print_r($payload, true));
|
||||
|
||||
return $this->json([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
// get event type and process accordingly
|
||||
$event_name = $payload['transaction_code'];
|
||||
|
||||
switch ($event_name) {
|
||||
case 'GR002':
|
||||
return $this->handleAuthenticated($payload);
|
||||
break;
|
||||
case 'GR003':
|
||||
return $this->handleUpdateMade($payload);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->json([
|
||||
'success' => true,
|
||||
'payload' => $payload,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function handleAuthenticated($payload)
|
||||
{
|
||||
$obj = $this->getApplication($payload['id']);
|
||||
$now = new DateTime();
|
||||
$expiry = DateTime::createFromFormat("Y-m-d", $payload['expiry_date']);
|
||||
|
||||
if (!empty($obj)) {
|
||||
// mark as completed
|
||||
$obj->setStatus(InsuranceApplicationStatus::COMPLETED);
|
||||
$obj->setDateComplete($now);
|
||||
$obj->setDateExpire($expiry);
|
||||
$obj->setCOC($payload['coc_url']);
|
||||
$this->em->flush();
|
||||
|
||||
// send notification
|
||||
$this->fcmclient->sendEvent($obj->getCustomer(), "insurance_fcm_title_completed", "insurance_fcm_body_completed", [
|
||||
'cv_id' => $obj->getCustomerVehicle()->getID(),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->json([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function handleUpdateMade($payload)
|
||||
{
|
||||
$obj = $this->getApplication($payload['id']);
|
||||
|
||||
if (!empty($obj)) {
|
||||
$metadata = $obj->getMetadata();
|
||||
|
||||
// initialize change list if not present
|
||||
if (empty($metadata['changes'])) {
|
||||
$metadata['changes'] = [];
|
||||
}
|
||||
|
||||
$now = new DateTime;
|
||||
$metadata['changes'][$now->format('Y-m-d H:i:s')] = $payload['data'];
|
||||
|
||||
// update metadata to record change
|
||||
$obj->setMetadata($metadata);
|
||||
$this->em->flush();
|
||||
|
||||
// send notification
|
||||
$this->fcmclient->sendEvent($obj->getCustomer(), "insurance_fcm_title_updated", "insurance_fcm_body_updated", [
|
||||
'cv_id' => $obj->getCustomerVehicle()->getID(),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->json([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getApplication($transaction_id)
|
||||
{
|
||||
$result = $this->em->getRepository(InsuranceApplication::class)->findBy([
|
||||
'ext_transaction_id' => $transaction_id,
|
||||
], [], 1);
|
||||
|
||||
return !empty($result) ? $result[0] : false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
110
src/Controller/PayMongoController.php
Normal file
110
src/Controller/PayMongoController.php
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\GatewayTransaction;
|
||||
use App\Ramcar\TransactionStatus;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
|
||||
class PayMongoController extends Controller
|
||||
{
|
||||
protected $em;
|
||||
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
public function listen(Request $req)
|
||||
{
|
||||
$payload = json_decode($req->getContent(), true);
|
||||
|
||||
// DEBUG
|
||||
@file_put_contents(__DIR__ . '/../../var/log/paymongo.log', print_r($payload, true) . "\r\n----------------------------------------\r\n\r\n", FILE_APPEND);
|
||||
|
||||
/*
|
||||
return $this->json([
|
||||
'success' => true,
|
||||
]);
|
||||
*/
|
||||
|
||||
// END DEBUG
|
||||
|
||||
// if no event type given, silently fail
|
||||
if (empty($payload['data'])) {
|
||||
error_log("Invalid paymongo callback received: " . print_r($payload, true));
|
||||
return $this->json([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
// get event type and process accordingly
|
||||
$attr = $payload['data']['attributes'];
|
||||
$event = $attr['data'];
|
||||
$event_name = $attr['type'];
|
||||
|
||||
switch ($event_name) {
|
||||
case "payment.paid":
|
||||
return $this->handlePaymentPaid($event);
|
||||
break;
|
||||
case "payment.failed":
|
||||
return $this->handlePaymentPaid($event);
|
||||
break;
|
||||
case "payment.refunded": // TODO: handle refunds
|
||||
case "payment.refund.updated":
|
||||
case "checkout_session.payment.paid":
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->json([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function handlePaymentPaid($event)
|
||||
{
|
||||
$metadata = $event['attributes']['metadata'];
|
||||
$obj = $this->getTransaction($metadata['transaction_id']);
|
||||
|
||||
if (!empty($obj)) {
|
||||
// mark as paid
|
||||
$obj->setStatus(TransactionStatus::PAID);
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
return $this->json([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function handlePaymentFailed(Request $req)
|
||||
{
|
||||
// TODO: do something about failed payments?
|
||||
return $this->json([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getTransaction($id)
|
||||
{
|
||||
//$class_name = 'App\\Entity\\' . $type;
|
||||
//$instance = new $class_name;
|
||||
|
||||
return $this->em->getRepository(GatewayTransaction::class)->find($id);
|
||||
}
|
||||
|
||||
public function paymentSuccess(Request $req)
|
||||
{
|
||||
return $this->render('paymongo/success.html.twig');
|
||||
}
|
||||
|
||||
public function paymentCancelled(Request $req)
|
||||
{
|
||||
return $this->render('paymongo/cancelled.html.twig');
|
||||
}
|
||||
}
|
||||
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Ramcar\InsuranceApplicationStatus;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use DateTime;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
|
|
@ -114,10 +116,15 @@ class CustomerVehicle
|
|||
*/
|
||||
protected $flag_active;
|
||||
|
||||
// link to insurance
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="InsuranceApplication", mappedBy="customer_vehicle")
|
||||
*/
|
||||
protected $insurance_applications;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->flag_active = true;
|
||||
|
||||
$this->job_orders = new ArrayCollection();
|
||||
}
|
||||
|
||||
|
|
@ -282,4 +289,21 @@ class CustomerVehicle
|
|||
{
|
||||
return $this->flag_active;
|
||||
}
|
||||
|
||||
public function getInsuranceApplications()
|
||||
{
|
||||
return $this->insurance_applications;
|
||||
}
|
||||
|
||||
public function getLatestInsuranceApplication()
|
||||
{
|
||||
$criteria = Criteria::create()
|
||||
->where(Criteria::expr()->notIn('status', [InsuranceApplicationStatus::CANCELLED]))
|
||||
->orderBy(['date_submit' => Criteria::DESC])
|
||||
->setMaxResults(1);
|
||||
|
||||
$result = $this->insurance_applications->matching($criteria);
|
||||
|
||||
return !empty($result) ? $result[0] : null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
193
src/Entity/GatewayTransaction.php
Normal file
193
src/Entity/GatewayTransaction.php
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use App\Ramcar\TransactionStatus;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="gateway_transaction")
|
||||
*/
|
||||
class GatewayTransaction
|
||||
{
|
||||
// unique id
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Customer", inversedBy="transactions")
|
||||
* @ORM\JoinColumn(name="customer_id", referencedColumnName="id")
|
||||
*/
|
||||
protected $customer;
|
||||
|
||||
// date ticket was created
|
||||
/**
|
||||
* @ORM\Column(type="datetime")
|
||||
*/
|
||||
protected $date_create;
|
||||
|
||||
// date ticket was paid
|
||||
/**
|
||||
* @ORM\Column(type="datetime", nullable=true)
|
||||
*/
|
||||
protected $date_pay;
|
||||
|
||||
// amount
|
||||
/**
|
||||
* @ORM\Column(type="bigint")
|
||||
* @Assert\NotBlank()
|
||||
*/
|
||||
protected $amount;
|
||||
|
||||
// status of the transaction
|
||||
/**
|
||||
* @ORM\Column(type="string", length=50)
|
||||
* @Assert\NotBlank()
|
||||
*/
|
||||
protected $status;
|
||||
|
||||
// type of transaction
|
||||
/**
|
||||
* @ORM\Column(type="string", length=50)
|
||||
* @Assert\NotBlank()
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
// gateway used for transaction
|
||||
/**
|
||||
* @ORM\Column(type="string", length=50)
|
||||
* @Assert\NotBlank()
|
||||
*/
|
||||
protected $gateway;
|
||||
|
||||
// external transaction id
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
protected $ext_transaction_id;
|
||||
|
||||
// other data related to the transaction
|
||||
/**
|
||||
* @ORM\Column(type="json")
|
||||
*/
|
||||
protected $metadata;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->date_create = new DateTime();
|
||||
$this->status = TransactionStatus::PENDING;
|
||||
$this->metadata = [];
|
||||
}
|
||||
|
||||
public function getID()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setCustomer(Customer $customer)
|
||||
{
|
||||
$this->customer = $customer;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCustomer()
|
||||
{
|
||||
return $this->customer;
|
||||
}
|
||||
|
||||
public function setDateCreate(DateTime $date)
|
||||
{
|
||||
$this->date_create = $date;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDateCreate()
|
||||
{
|
||||
return $this->date_create;
|
||||
}
|
||||
|
||||
public function setDatePay(DateTime $date)
|
||||
{
|
||||
$this->date_pay = $date;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDatePay()
|
||||
{
|
||||
return $this->date_pay;
|
||||
}
|
||||
|
||||
public function setAmount($amount)
|
||||
{
|
||||
$this->amount = $amount;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAmount()
|
||||
{
|
||||
return $this->amount;
|
||||
}
|
||||
|
||||
public function setStatus($status)
|
||||
{
|
||||
$this->status = $status;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStatus()
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
public function setType($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function setGateway($gateway)
|
||||
{
|
||||
$this->gateway = $gateway;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getGateway()
|
||||
{
|
||||
return $this->gateway;
|
||||
}
|
||||
|
||||
public function setExtTransactionId($transaction_id)
|
||||
{
|
||||
$this->ext_transaction_id = $transaction_id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getExtTransactionId()
|
||||
{
|
||||
return $this->ext_transaction_id;
|
||||
}
|
||||
|
||||
public function setMetadata($metadata)
|
||||
{
|
||||
$this->metadata = $metadata;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMetadata()
|
||||
{
|
||||
return $this->metadata;
|
||||
}
|
||||
}
|
||||
226
src/Entity/InsuranceApplication.php
Normal file
226
src/Entity/InsuranceApplication.php
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
<?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="insurance_application")
|
||||
*/
|
||||
class InsuranceApplication
|
||||
{
|
||||
// unique id
|
||||
/**
|
||||
* @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;
|
||||
|
||||
// link to customer vehicle
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="CustomerVehicle", inversedBy="insurance")
|
||||
* @ORM\JoinColumn(name="customer_vehicle_id", referencedColumnName="id")
|
||||
* @Assert\NotBlank()
|
||||
*/
|
||||
protected $customer_vehicle;
|
||||
|
||||
// gateway transaction
|
||||
/**
|
||||
* @ORM\OneToOne(targetEntity="GatewayTransaction")
|
||||
* @ORM\JoinColumn(name="gateway_transaction_id", referencedColumnName="id")
|
||||
*/
|
||||
protected $gateway_transaction;
|
||||
|
||||
// status
|
||||
/**
|
||||
* @ORM\Column(type="string", length=32)
|
||||
*/
|
||||
protected $status;
|
||||
|
||||
// URL of COC
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
protected $coc_url;
|
||||
|
||||
// date the application was submitted
|
||||
/**
|
||||
* @ORM\Column(type="datetime")
|
||||
*/
|
||||
protected $date_submit;
|
||||
|
||||
// date the application was paid
|
||||
/**
|
||||
* @ORM\Column(type="datetime", nullable=true)
|
||||
*/
|
||||
protected $date_pay;
|
||||
|
||||
// date the application was marked as completed by the insurance api
|
||||
/**
|
||||
* @ORM\Column(type="datetime", nullable=true)
|
||||
*/
|
||||
protected $date_complete;
|
||||
|
||||
// date the application is set to expire
|
||||
/**
|
||||
* @ORM\Column(type="datetime", nullable=true)
|
||||
*/
|
||||
protected $date_expire;
|
||||
|
||||
// external transaction id
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
protected $ext_transaction_id;
|
||||
|
||||
// form data when submitting the application
|
||||
/**
|
||||
* @ORM\Column(type="json")
|
||||
*/
|
||||
protected $metadata;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->date_submit = new DateTime();
|
||||
$this->date_pay = null;
|
||||
$this->date_complete = null;
|
||||
$this->date_expire = 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 $cv = null)
|
||||
{
|
||||
$this->customer_vehicle = $cv;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCustomerVehicle()
|
||||
{
|
||||
return $this->customer_vehicle;
|
||||
}
|
||||
|
||||
public function setDateSubmit(DateTime $date)
|
||||
{
|
||||
$this->date_submit = $date;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDateSubmit()
|
||||
{
|
||||
return $this->date_submit;
|
||||
}
|
||||
|
||||
public function setGatewayTransaction(GatewayTransaction $transaction)
|
||||
{
|
||||
$this->gateway_transaction = $transaction;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getGatewayTransaction()
|
||||
{
|
||||
return $this->gateway_transaction;
|
||||
}
|
||||
|
||||
public function setStatus($status)
|
||||
{
|
||||
return $this->status = $status;
|
||||
}
|
||||
|
||||
public function getStatus()
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
public function setCOC($url)
|
||||
{
|
||||
return $this->coc_url = $url;
|
||||
}
|
||||
|
||||
public function getCOC()
|
||||
{
|
||||
return $this->coc_url;
|
||||
}
|
||||
|
||||
public function setDatePay(DateTime $date)
|
||||
{
|
||||
$this->date_pay = $date;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDatePay()
|
||||
{
|
||||
return $this->date_pay;
|
||||
}
|
||||
|
||||
public function setDateComplete(DateTime $date)
|
||||
{
|
||||
$this->date_complete = $date;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDateComplete()
|
||||
{
|
||||
return $this->date_complete;
|
||||
}
|
||||
|
||||
public function setDateExpire(DateTime $date)
|
||||
{
|
||||
$this->date_expire = $date;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDateExpire()
|
||||
{
|
||||
return $this->date_expire;
|
||||
}
|
||||
|
||||
public function setExtTransactionId($transaction_id)
|
||||
{
|
||||
$this->ext_transaction_id = $transaction_id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getExtTransactionId()
|
||||
{
|
||||
return $this->ext_transaction_id;
|
||||
}
|
||||
|
||||
public function setMetadata($metadata)
|
||||
{
|
||||
return $this->metadata = $metadata;
|
||||
}
|
||||
|
||||
public function getMetadata()
|
||||
{
|
||||
return $this->metadata;
|
||||
}
|
||||
}
|
||||
75
src/EntityListener/GatewayTransactionListener.php
Normal file
75
src/EntityListener/GatewayTransactionListener.php
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace App\EntityListener;
|
||||
|
||||
use Doctrine\ORM\Event\LifecycleEventArgs;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
use App\Entity\GatewayTransaction;
|
||||
use App\Entity\InsuranceApplication;
|
||||
use App\Service\InsuranceConnector;
|
||||
use App\Ramcar\InsuranceApplicationStatus;
|
||||
use App\Ramcar\TransactionStatus;
|
||||
use DateTime;
|
||||
|
||||
class GatewayTransactionListener
|
||||
{
|
||||
protected $ic;
|
||||
protected $em;
|
||||
|
||||
public function __construct(EntityManagerInterface $em, InsuranceConnector $ic)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->ic = $ic;
|
||||
}
|
||||
|
||||
public function postUpdate(GatewayTransaction $gt_obj, LifecycleEventArgs $args)
|
||||
{
|
||||
// get transaction changes
|
||||
$em = $args->getEntityManager();
|
||||
$uow = $em->getUnitOfWork();
|
||||
$changeset = $uow->getEntityChangeSet($gt_obj);
|
||||
|
||||
if (array_key_exists('status', $changeset)) {
|
||||
$field_changes = $changeset['status'];
|
||||
|
||||
$prev_value = $field_changes[0] ?? null;
|
||||
$new_value = $field_changes[1] ?? null;
|
||||
|
||||
// only do something if the status has changed to paid
|
||||
if ($prev_value !== $new_value && $new_value === TransactionStatus::PAID) {
|
||||
// handle based on type
|
||||
// TODO: add types here as we go. there's probably a better way to do this.
|
||||
switch ($gt_obj->getType()) {
|
||||
case 'insurance_premium':
|
||||
return $this->handleInsurancePremium($gt_obj);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function handleInsurancePremium($gt_obj)
|
||||
{
|
||||
// get insurance application object
|
||||
$obj = $this->em->getRepository(InsuranceApplication::class)->findOneBy([
|
||||
'gateway_transaction' => $gt_obj,
|
||||
]);
|
||||
|
||||
if (!empty($obj)) {
|
||||
// mark as paid
|
||||
$obj->setDatePay(new DateTime());
|
||||
$obj->setStatus(InsuranceApplicationStatus::PAID);
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
// flag on api as paid
|
||||
$result = $this->ic->tagApplicationPaid($obj->getID());
|
||||
if (!$result['success'] || $result['response']['transaction_code'] !== 'GR004') {
|
||||
error_log("INSURANCE MARK AS PAID FAILED FOR " . $obj->getID() . ": " . $result['error']['message']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
18
src/Ramcar/InsuranceApplicationStatus.php
Normal file
18
src/Ramcar/InsuranceApplicationStatus.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App\Ramcar;
|
||||
|
||||
class InsuranceApplicationStatus extends NameValue
|
||||
{
|
||||
const CREATED = 'created';
|
||||
const PAID = 'paid';
|
||||
const COMPLETED = 'completed';
|
||||
const CANCELLED = 'cancelled';
|
||||
|
||||
const COLLECTION = [
|
||||
'created' => 'Created',
|
||||
'paid' => 'Paid',
|
||||
'completed' => 'Completed',
|
||||
'cancelled' => 'Cancelled',
|
||||
];
|
||||
}
|
||||
14
src/Ramcar/InsuranceClientType.php
Normal file
14
src/Ramcar/InsuranceClientType.php
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace App\Ramcar;
|
||||
|
||||
class InsuranceClientType extends NameValue
|
||||
{
|
||||
const INDIVIDUAL = 'i';
|
||||
const CORPORATE = 'c';
|
||||
|
||||
const COLLECTION = [
|
||||
'i' => 'Individual',
|
||||
'c' => 'Corporate',
|
||||
];
|
||||
}
|
||||
32
src/Ramcar/InsuranceMVType.php
Normal file
32
src/Ramcar/InsuranceMVType.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace App\Ramcar;
|
||||
|
||||
class InsuranceMVType extends NameValue
|
||||
{
|
||||
const CAR = '1';
|
||||
const SHUTTLE_BUS = '2';
|
||||
const MOTORCYCLE = '3';
|
||||
const MC_WITH_SIDECAR = '4';
|
||||
const NON_CONVENTIONAL = '5';
|
||||
const SUV = '8';
|
||||
const TRUCK = '9';
|
||||
const TRAILER = '10';
|
||||
const UV_PRIVATE = '11';
|
||||
const UV_COMMERCIAL = '12';
|
||||
const TRICYCLE = '13';
|
||||
|
||||
const COLLECTION = [
|
||||
'1' => "Car",
|
||||
'2' => "Shuttle Bus",
|
||||
'3' => "Motorcycle",
|
||||
'4' => "Motorcycle with Sidecar",
|
||||
'5' => "Non-Conventional MV",
|
||||
'8' => "Sports Utility Vehicle",
|
||||
'9' => "Truck",
|
||||
'10' => "Trailer",
|
||||
'11' => "UV Private",
|
||||
'12' => "UV Commercial",
|
||||
'13' => "Tricycle",
|
||||
];
|
||||
}
|
||||
18
src/Ramcar/InsuranceVehicleLine.php
Normal file
18
src/Ramcar/InsuranceVehicleLine.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App\Ramcar;
|
||||
|
||||
class InsuranceVehicleLine extends NameValue
|
||||
{
|
||||
const PCOC = 'pcoc';
|
||||
const MCOC = 'mcoc';
|
||||
const CCOC = 'ccoc';
|
||||
const LCOC = 'lcoc';
|
||||
|
||||
const COLLECTION = [
|
||||
'pcoc' => 'Private Car',
|
||||
'mcoc' => 'Motorcycle / Motorcycle with Sidecar / Tricycle (Private)',
|
||||
'ccoc' => 'Commercial Vehicle',
|
||||
'lcoc' => 'Motorcycle / Motorcycle with Sidecar / Tricycle (Public)',
|
||||
];
|
||||
}
|
||||
18
src/Ramcar/TransactionStatus.php
Normal file
18
src/Ramcar/TransactionStatus.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App\Ramcar;
|
||||
|
||||
class TransactionStatus extends NameValue
|
||||
{
|
||||
const PENDING = 'pending';
|
||||
const PAID = 'paid';
|
||||
const CANCELLED = 'cancelled';
|
||||
const REFUNDED = 'refunded';
|
||||
|
||||
const COLLECTION = [
|
||||
'pending' => 'Pending',
|
||||
'paid' => 'Paid',
|
||||
'cancelled' => 'Cancelled',
|
||||
'refunded' => 'Refunded',
|
||||
];
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\Customer;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use Fcm\FcmClient;
|
||||
use Fcm\Push\Notification;
|
||||
|
|
@ -53,42 +54,62 @@ class FCMSender
|
|||
|
||||
public function sendJoEvent(JobOrder $job_order, $title, $body, $data = [])
|
||||
{
|
||||
// get all v2 sessions
|
||||
// get customer object
|
||||
$cust = $job_order->getCustomer();
|
||||
|
||||
// attach jo info
|
||||
$data['jo_id'] = $job_order->getID();
|
||||
$data['jo_status'] = $job_order->getStatus();
|
||||
|
||||
// send the event
|
||||
return $this->sendEvent($cust, $title, $body, $data);
|
||||
}
|
||||
|
||||
public function sendEvent(Customer $cust, $title, $body, $data = [])
|
||||
{
|
||||
// get all v2 devices
|
||||
$devices = $this->getDevices($cust);
|
||||
|
||||
if (empty($devices)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// send fcm notification
|
||||
$result = $this->send(array_keys($devices), $this->translator->trans($title), $this->translator->trans($body), $data);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function getDevices(Customer $cust)
|
||||
{
|
||||
$sessions = [];
|
||||
$cust_user = $job_order->getCustomer()->getCustomerUser();
|
||||
$device_ids = [];
|
||||
|
||||
$cust_user = $cust->getCustomerUser();
|
||||
if (!empty($cust_user)) {
|
||||
$sessions = $cust_user->getMobileSessions();
|
||||
}
|
||||
|
||||
if (empty($sessions)) {
|
||||
error_log("no sessions to send fcm notification to");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
$device_ids = [];
|
||||
|
||||
// send to every customer session
|
||||
foreach ($sessions as $sess) {
|
||||
$device_id = $sess->getDevicePushID();
|
||||
|
||||
if (!empty($device_id) && !isset($device_ids[$device_id])) {
|
||||
// send fcm notification
|
||||
// send to this device
|
||||
$device_ids[$device_id] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($device_ids)) {
|
||||
error_log("no devices to send fcm notification to");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// attach jo info
|
||||
$data['jo_id'] = $job_order->getID();
|
||||
$data['jo_status'] = $job_order->getStatus();
|
||||
|
||||
// send fcm notification
|
||||
$result = $this->send(array_keys($device_ids), $this->translator->trans($title), $this->translator->trans($body), $data);
|
||||
|
||||
return $result;
|
||||
return $device_ids;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\CustomerVehicle;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
|
||||
class InsuranceConnector
|
||||
{
|
||||
protected $base_url;
|
||||
|
|
@ -17,37 +22,68 @@ class InsuranceConnector
|
|||
$this->hash = $this->generateHash();
|
||||
}
|
||||
|
||||
public function createApplication($notif_url, $client_info, $client_contact_info, $car_info)
|
||||
public function createApplication(CustomerVehicle $cv, $notif_url, $data, $orcr_file)
|
||||
{
|
||||
$body = [
|
||||
'notif_url' => $notif_url,
|
||||
'client_info' => $client_info,
|
||||
'client_contact_info' => $client_contact_info,
|
||||
'car_info' => $car_info,
|
||||
'client_info' => [
|
||||
'client_type' => $data['client_type'],
|
||||
'first_name' => $data['first_name'],
|
||||
'middle_name' => $data['middle_name'] ?? null,
|
||||
'surname' => $data['surname'],
|
||||
'corporate_name' => $data['corporate_name'],
|
||||
],
|
||||
'client_contact_info' => [
|
||||
'address_number' => $data['address_number'],
|
||||
'address_street' => $data['address_street'] ?? null,
|
||||
'address_building' => $data['address_building'] ?? null,
|
||||
'address_barangay' => $data['address_barangay'],
|
||||
'address_city' => $data['address_city'],
|
||||
'address_province' => $data['address_province'],
|
||||
'zipcode' => (int)$data['zipcode'],
|
||||
'mobile_number' => $data['mobile_number'],
|
||||
'email_address' => $data['email_address'],
|
||||
],
|
||||
'car_info' => [
|
||||
'make' => $data['make'],
|
||||
'model' => $data['model'],
|
||||
'series' => $data['series'],
|
||||
'color' => $data['color'],
|
||||
'plate_number' => $cv->getPlateNumber(),
|
||||
'mv_file_number' => $data['mv_file_number'],
|
||||
'motor_number' => $data['motor_number'],
|
||||
'serial_chasis' => $data['serial_chasis'],
|
||||
'year_model' => (int)$data['year_model'],
|
||||
'mv_type_id' => (int)$data['mv_type_id'],
|
||||
'body_type' => $data['body_type'],
|
||||
'is_public' => (bool)$data['is_public'],
|
||||
'line' => $data['line'],
|
||||
'orcr_file' => base64_encode(file_get_contents($orcr_file->getPathname())),
|
||||
],
|
||||
];
|
||||
|
||||
return $this->doRequest('/api/v1/ctpl/applications', true, $body);
|
||||
return $this->doRequest('/api/v1/ctpl/applications', 'POST', $body);
|
||||
}
|
||||
|
||||
public function tagApplicationPaid($application_id)
|
||||
{
|
||||
$url = '/api/v1/ctpl/application/' . $application_id . '/paid';
|
||||
return $this->doRequest($url, true);
|
||||
return $this->doRequest($url, 'POST');
|
||||
}
|
||||
|
||||
public function getVehicleMakers()
|
||||
{
|
||||
return $this->doRequest('/api/v1/ctpl/vehicle-makers');
|
||||
return $this->doRequest('/api/v1/ctpl/vehicle-makers', 'GET');
|
||||
}
|
||||
|
||||
public function getVehicleModels()
|
||||
public function getVehicleModels($maker_id)
|
||||
{
|
||||
return $this->doRequest('/api/v1/ctpl/vehicle-models');
|
||||
return $this->doRequest('/api/v1/ctpl/vehicle-models?maker_id='. $maker_id, 'GET');
|
||||
}
|
||||
|
||||
public function getVehicleTrims()
|
||||
public function getVehicleTrims($model_id)
|
||||
{
|
||||
return $this->doRequest('/api/v1/ctpl/vehicle-trims');
|
||||
return $this->doRequest('/api/v1/ctpl/vehicle-trims?model_id='. $model_id, 'GET');
|
||||
}
|
||||
|
||||
protected function generateHash()
|
||||
|
|
@ -55,46 +91,42 @@ class InsuranceConnector
|
|||
return base64_encode($this->username . ":" . $this->password);
|
||||
}
|
||||
|
||||
protected function doRequest($url, $is_post = false, $body = [])
|
||||
protected function doRequest($url, $method, $body = [])
|
||||
{
|
||||
$curl = curl_init();
|
||||
|
||||
$options = [
|
||||
CURLOPT_URL => $this->base_url . '/' . $url,
|
||||
CURLOPT_POST => $is_post,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Content-Type: application/json',
|
||||
'Authorization: Basic ' . $this->hash,
|
||||
],
|
||||
$client = new Client();
|
||||
$headers = [
|
||||
'Content-Type' => 'application/json',
|
||||
'accept' => 'application/json',
|
||||
'authorization' => 'Basic '. $this->hash,
|
||||
];
|
||||
|
||||
// add post body if present
|
||||
if (!empty($body)) {
|
||||
$options[CURLOPT_POSTFIELDS] = json_encode($body);
|
||||
try {
|
||||
$response = $client->request($method, $this->base_url . '/' . $url, [
|
||||
'json' => $body,
|
||||
'headers' => $headers,
|
||||
]);
|
||||
} catch (RequestException $e) {
|
||||
$error = ['message' => $e->getMessage()];
|
||||
|
||||
error_log("Insurance API Error: " . $error['message']);
|
||||
error_log(Psr7\Message::toString($e->getRequest()));
|
||||
error_log($e->getResponse()->getBody()->getContents());
|
||||
|
||||
if ($e->hasResponse()) {
|
||||
$error['response'] = Psr7\Message::toString($e->getResponse());
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => $error,
|
||||
];
|
||||
}
|
||||
|
||||
curl_setopt_array($curl, $options);
|
||||
$res = curl_exec($curl);
|
||||
error_log(print_r(json_decode($response->getBody(), true), true));
|
||||
|
||||
curl_close($curl);
|
||||
|
||||
error_log('Insurance API connector');
|
||||
error_log(print_r($options, true));
|
||||
error_log($res);
|
||||
|
||||
// response
|
||||
return $this->handleResponse($res);
|
||||
}
|
||||
|
||||
protected function handleResponse($res)
|
||||
{
|
||||
$inv_res = json_decode($res, true);
|
||||
|
||||
// make sure result is always an array
|
||||
if ($inv_res == null)
|
||||
return [];
|
||||
|
||||
return $inv_res;
|
||||
return [
|
||||
'success' => true,
|
||||
'response' => json_decode($response->getBody(), true)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
125
src/Service/PayMongoConnector.php
Normal file
125
src/Service/PayMongoConnector.php
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
|
||||
use App\Entity\Customer;
|
||||
|
||||
class PayMongoConnector
|
||||
{
|
||||
protected $base_url;
|
||||
protected $public_key;
|
||||
protected $secret_key;
|
||||
protected $hash;
|
||||
|
||||
public function __construct($base_url, $public_key, $secret_key)
|
||||
{
|
||||
$this->base_url = $base_url;
|
||||
$this->public_key = $public_key;
|
||||
$this->secret_key = $secret_key;
|
||||
$this->hash = $this->generateHash();
|
||||
}
|
||||
|
||||
public function createCheckout(Customer $cust, $items, $ref_no = null, $description = null, $success_url = null, $cancel_url = null, $metadata = [])
|
||||
{
|
||||
// build billing info
|
||||
$billing = [
|
||||
'name' => implode(" ", [$cust->getFirstName(), $cust->getLastName()]),
|
||||
'phone' => $cust->getPhoneMobile(),
|
||||
];
|
||||
|
||||
if ($cust->getEmail()) {
|
||||
$billing['email'] = $cust->getEmail();
|
||||
}
|
||||
|
||||
// build the request body
|
||||
$body = [
|
||||
'data' => [
|
||||
'attributes' => [
|
||||
'description' => $description,
|
||||
'billing' => $billing,
|
||||
// NOTE: this may be variable later, hardcoding for now
|
||||
'payment_method_types' => [
|
||||
'card',
|
||||
'paymaya',
|
||||
'gcash',
|
||||
],
|
||||
/* NOTE: format for line items:
|
||||
* ['name', 'description', 'quantity', 'amount', 'currency']
|
||||
*/
|
||||
'line_items' => $items,
|
||||
'reference_number' => (string)$ref_no,
|
||||
'cancel_url' => $cancel_url,
|
||||
'success_url' => $success_url,
|
||||
'statement_descriptor' => $description,
|
||||
'send_email_receipt' => true,
|
||||
'show_description' => true,
|
||||
'show_line_items' => false,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
if (!empty($metadata)) {
|
||||
$body['data']['attributes']['metadata'] = $metadata;
|
||||
}
|
||||
|
||||
return $this->doRequest('/v1/checkout_sessions', 'POST', $body);
|
||||
}
|
||||
|
||||
public function getCheckout($checkout_id)
|
||||
{
|
||||
return $this->doRequest('/v1/checkout_sessions/' . $checkout_id, 'GET');
|
||||
}
|
||||
|
||||
protected function generateHash()
|
||||
{
|
||||
return base64_encode($this->secret_key);
|
||||
}
|
||||
|
||||
protected function doRequest($url, $method, $body = [])
|
||||
{
|
||||
$client = new Client();
|
||||
$headers = [
|
||||
'Content-Type' => 'application/json',
|
||||
'accept' => 'application/json',
|
||||
'authorization' => 'Basic '. $this->hash,
|
||||
];
|
||||
|
||||
try {
|
||||
$response = $client->request($method, $this->base_url . '/' . $url, [
|
||||
'json' => $body,
|
||||
'headers' => $headers,
|
||||
]);
|
||||
} catch (RequestException $e) {
|
||||
$error = ['message' => $e->getMessage()];
|
||||
|
||||
ob_start();
|
||||
var_dump($body);
|
||||
$varres = ob_get_clean();
|
||||
error_log($varres);
|
||||
|
||||
error_log("--------------------------------------");
|
||||
error_log($e->getResponse()->getBody()->getContents());
|
||||
|
||||
error_log("PayMongo API Error: " . $error['message']);
|
||||
error_log(Psr7\Message::toString($e->getRequest()));
|
||||
|
||||
if ($e->hasResponse()) {
|
||||
$error['response'] = Psr7\Message::toString($e->getResponse());
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => $error,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'response' => json_decode($response->getBody(), true)
|
||||
];
|
||||
}
|
||||
}
|
||||
22
templates/paymongo/cancelled.html.twig
Normal file
22
templates/paymongo/cancelled.html.twig
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Payment Cancelled</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #333;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
window.addEventListener('load', (e) => {
|
||||
if (typeof toApp !== 'undefined') {
|
||||
toApp.postMessage("paymentCancelled");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
22
templates/paymongo/success.html.twig
Normal file
22
templates/paymongo/success.html.twig
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Payment Successful</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #333;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
window.addEventListener('load', (e) => {
|
||||
if (typeof toApp !== 'undefined') {
|
||||
toApp.postMessage("paymentSuccess");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -161,13 +161,19 @@ menu.database.ownershiptypes: 'Ownership Types'
|
|||
menu.database.serviceofferings: 'Service Offerings'
|
||||
|
||||
# fcm jo status updates
|
||||
jo_fcm_title_outlet_assign: Looking for riders
|
||||
jo_fcm_title_driver_assigned: Rider found
|
||||
jo_fcm_title_driver_arrived: Rider nearby
|
||||
jo_fcm_title_cancelled: Order cancelled
|
||||
jo_fcm_title_fulfilled: Thank you!
|
||||
jo_fcm_body_outlet_assign: We're assigning a rider for your order, please wait.
|
||||
jo_fcm_body_driver_assigned: A rider is on their way.
|
||||
jo_fcm_body_driver_arrived: Your order is almost there!
|
||||
jo_fcm_body_cancelled: Your order has been cancelled.
|
||||
jo_fcm_body_fulfilled: Order complete! Your receipt is ready.
|
||||
jo_fcm_title_outlet_assign: 'Looking for riders'
|
||||
jo_fcm_title_driver_assigned: 'Rider found'
|
||||
jo_fcm_title_driver_arrived: 'Rider nearby'
|
||||
jo_fcm_title_cancelled: 'Order cancelled'
|
||||
jo_fcm_title_fulfilled: 'Thank you!'
|
||||
jo_fcm_body_outlet_assign: 'We`re assigning a rider for your order, please wait.'
|
||||
jo_fcm_body_driver_assigned: 'A rider is on their way.'
|
||||
jo_fcm_body_driver_arrived: 'Your order is almost there!'
|
||||
jo_fcm_body_cancelled: 'Your order has been cancelled.'
|
||||
jo_fcm_body_fulfilled: 'Order complete! Your receipt is ready.'
|
||||
|
||||
# fcm insurance
|
||||
insurance_fcm_title_updated: 'Application updated'
|
||||
insurance_fcm_title_completed: 'Application completed'
|
||||
insurance_fcm_body_updated: 'Some details on your insurance application have been updated.'
|
||||
insurance_fcm_body_completed: 'Your insurance application has been processed!'
|
||||
|
|
|
|||
Loading…
Reference in a new issue