<?php
namespace App\Logic\Paypal;
use Exception;
use App\Setting;
use PayPal\Api\Currency;
use PayPal\Api\MerchantPreferences;
use PayPal\Api\PaymentDefinition;
use PayPal\Api\Plan;
use PayPal\Api\Patch;
use PayPal\Api\PatchRequest;
use PayPal\Api\VerifyWebhookSignature;
use PayPal\Api\Webhook;
use PayPal\Auth\OAuthTokenCredential;
use PayPal\Common\PayPalModel;
use PayPal\Api\Agreement;
use PayPal\Api\Payer;
use PayPal\Rest\ApiContext;
class Paypal {
public $apiContext;
public function __construct()
{
$this->apiContext = new ApiContext(
new OAuthTokenCredential(
config('services.paypal.client_id'), // ClientID
config('services.paypal.secret')
)
);
}
public function createPlan()
{
$monthlyAmount = 10;
$currency = 'EUR';
// Create a new instance of Plan object
$plan = new Plan();
// # Basic Information
// Fill up the basic information that is required for the plan
$plan->setName('Epic Plan')
->setDescription('Unlimited form submissions. €10 paid monthly.')
->setType('infinite');
// # Payment definitions for this billing plan.
$paymentDefinition = new PaymentDefinition();
$paymentDefinition->setName('Regular Payments')
->setType('REGULAR')
->setFrequency('Day')
->setFrequencyInterval("1")
->setCycles("0")
->setAmount(new Currency(array('value' => $monthlyAmount, 'currency' => $currency)));
$merchantPreferences = new MerchantPreferences();
$baseUrl = env('APP_URL');
$merchantPreferences->setReturnUrl("$baseUrl/paypal/execute-agreement?success=true")
->setCancelUrl("$baseUrl/paypal/execute-agreement?success=false")
->setAutoBillAmount("yes")
->setInitialFailAmountAction("CONTINUE")
->setMaxFailAttempts("0")
->setSetupFee(new Currency(array('value' => $monthlyAmount, 'currency' => $currency)));
$plan->setPaymentDefinitions(array($paymentDefinition));
$plan->setMerchantPreferences($merchantPreferences);
// ### Create Plan
$output = $plan->create($this->apiContext);
return $output;
}
public function activatePlan()
{
$createdPlan = $this->createPlan();
$patch = new Patch();
$value = new PayPalModel('{
"state":"ACTIVE"
}');
$patch->setOp('replace')
->setPath('/')
->setValue($value);
$patchRequest = new PatchRequest();
$patchRequest->addPatch($patch);
$createdPlan->update($patchRequest, $this->apiContext);
$plan = Plan::get($createdPlan->getId(), $this->apiContext);
// try to find previous setting with plan id
$setting = Setting::where('name', '=', 'active_plan_id')->first();
// save the plan id in the database
if ($setting) {
$setting->value = $plan->getId();
$setting->save();
} else {
$newSetting = new Setting(
[
'name' => 'active_plan_id',
'value' => $plan->getId(),
]
);
$newSetting->save();
}
return $plan;
}
public function createBillingAgreement()
{
$setting = Setting::where('name', '=', 'active_plan_id')->first();
if (! $setting) {
throw new Exception('Active plan id not found.');
}
$activePlanId = $setting->value;
$agreement = new Agreement();
$dateAgreementStarts = gmdate("Y-m-d\TH:i:s\Z", time() + 120);
$agreement->setName('Base Agreement')
->setDescription('€10 a month in return for accessing our unlimited plan.')
->setStartDate($dateAgreementStarts);
// Add Plan ID
// Please note that the plan Id should be only set in this case.
$plan = new Plan();
$plan->setId($activePlanId);
$agreement->setPlan($plan);
// Add Payer
$payer = new Payer();
$payer->setPaymentMethod('paypal');
$agreement->setPayer($payer);
// ### Create Agreement
// Please note that as the agreement has not yet activated, we wont be receiving the ID just yet.
$agreement = $agreement->create($this->apiContext);
// ### Get redirect url
$approvalUrl = $agreement->getApprovalLink();
return redirect($approvalUrl);
}
/**
* Get list of all webhooks
*
* @return \PayPal\Api\WebhookList
*/
public function listAllWebhooks()
{
$output = Webhook::getAllWithParams([], $this->apiContext);
return $output;
}
/**
* Delete all webhooks
*
* @return bool
*/
public function deleteAllWebhooks()
{
$webhookList = $this->listAllWebhooks();
foreach ($webhookList->getWebhooks() as $webhook) {
$webhook->delete($this->apiContext);
}
return true;
}
/**
* Register primary webhooks
*
* @return Webhook|string
*/
public function registerPrimaryWebhooks()
{
$setting = Setting::where('name', '=', 'primary_webhooks_registered')->first();
if ($setting) {
return 'primary webhooks already registered';
}
$baseUrl = env('APP_URL');
$webhook = new Webhook();
$webhook->setUrl("$baseUrl/paypal/webhooks/primary");
// # Event Types
// Event types correspond to what kind of notifications you want to receive on the given URL.
$webhookEventTypes = array();
$webhookEventTypes[] = new \PayPal\Api\WebhookEventType(
'{
"name":"PAYMENT.AUTHORIZATION.CREATED"
}'
);
$webhookEventTypes[] = new \PayPal\Api\WebhookEventType(
'{
"name":"PAYMENT.AUTHORIZATION.VOIDED"
}'
);
$webhook->setEventTypes($webhookEventTypes);
// ### Create Webhook
$output = $webhook->create($this->apiContext);
$newSetting = new Setting(
[
'name' => 'primary_webhooks_registered',
'value' => $output->getId(),
]
);
$newSetting->save();
return $output;
}
public function validatePrimaryWebhooks()
{
$setting = Setting::where('name', '=', 'primary_webhooks_registered')->first();
if (! $setting) {
throw new Exception('Primary webhooks not registered.');
}
$webhookId = $setting->value;
$requestBody = file_get_contents('php://input');
$headers = getallheaders();
$headers = array_change_key_case($headers, CASE_UPPER);
$signatureVerification = new VerifyWebhookSignature();
$signatureVerification->setAuthAlgo($headers['PAYPAL-AUTH-ALGO']);
$signatureVerification->setTransmissionId($headers['PAYPAL-TRANSMISSION-ID']);
$signatureVerification->setCertUrl($headers['PAYPAL-CERT-URL']);
$signatureVerification->setWebhookId($webhookId);
$signatureVerification->setTransmissionSig($headers['PAYPAL-TRANSMISSION-SIG']);
$signatureVerification->setTransmissionTime($headers['PAYPAL-TRANSMISSION-TIME']);
$signatureVerification->setRequestBody($requestBody);
$output = $signatureVerification->post($this->apiContext);
$status = $output->getVerificationStatus();
(new Setting(
[
'name' => 'webhook ' . date('Y-m-d H:i:s'),
'value' => $status . $requestBody,
]
))->save();
return response()->json([
'status' => 'OK'
]);
}
}