Today, I wanted to get a fresh Laravel 8 install on a project I started recently, basically restarting, as I needed the teams functionality of Jetstream and you can’t add the teams functionality into a project which already has Jetstream. So, I tried creating a fresh Laravel install and copying the files over to my project but for some reason the MySQL container didn’t get installed, which was confusing at first. Anyway, I ended up deleting the ‘vendor’ folder and installing everything a new with composer and then running:
phpartisansail:install
then I run
./vendor/bin/sailbuild--no-cache
and then just
./vendor/bin/sailup
this time MySQL worked and I could run my migrations.
I am sharing this in case someone else encounters a similar problem.
Recently, I started trying to integrate PayPal subsriptions into a project of mine, however, it turns out I’ve started by using the old PayPal PHP SDK which is not the recommended way anymore. The recommended way is using the new Checkout PHP SDK, however that one doesn’t support subscriptions so the way to integrate PayPal subscriptions is by making direct https calls to their API. In this post I’ll show you how I started integrating the subscriptions using the old, deprecated SDK in case somebody needs it. Note that it’s not full integration (I’ve only gotten to a certain point) and it’s also Laravel specific.
<?phpnamespaceApp\Logic\Paypal;useException;useApp\Setting;usePayPal\Api\Currency;usePayPal\Api\MerchantPreferences;usePayPal\Api\PaymentDefinition;usePayPal\Api\Plan;usePayPal\Api\Patch;usePayPal\Api\PatchRequest;usePayPal\Api\VerifyWebhookSignature;usePayPal\Api\Webhook;usePayPal\Auth\OAuthTokenCredential;usePayPal\Common\PayPalModel;usePayPal\Api\Agreement;usePayPal\Api\Payer;usePayPal\Rest\ApiContext;classPaypal {public $apiContext;publicfunction__construct() {$this->apiContext =newApiContext(newOAuthTokenCredential(config('services.paypal.client_id'), // ClientIDconfig('services.paypal.secret') ) ); }publicfunctioncreatePlan() { $monthlyAmount =10; $currency ='EUR';// Create a new instance of Plan object $plan =newPlan();// # 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 =newPaymentDefinition(); $paymentDefinition->setName('Regular Payments')->setType('REGULAR')->setFrequency('Day')->setFrequencyInterval("1")->setCycles("0")->setAmount(newCurrency(array('value'=> $monthlyAmount, 'currency'=> $currency))); $merchantPreferences =newMerchantPreferences(); $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(newCurrency(array('value'=> $monthlyAmount, 'currency'=> $currency))); $plan->setPaymentDefinitions(array($paymentDefinition)); $plan->setMerchantPreferences($merchantPreferences);// ### Create Plan $output = $plan->create($this->apiContext);return $output; }publicfunctionactivatePlan() { $createdPlan =$this->createPlan(); $patch =newPatch(); $value =newPayPalModel('{ "state":"ACTIVE" }'); $patch->setOp('replace')->setPath('/')->setValue($value); $patchRequest =newPatchRequest(); $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 databaseif ($setting) { $setting->value = $plan->getId(); $setting->save(); } else { $newSetting =newSetting( ['name'=>'active_plan_id','value'=> $plan->getId(), ] ); $newSetting->save(); }return $plan; }publicfunctioncreateBillingAgreement() { $setting =Setting::where('name', '=', 'active_plan_id')->first();if (! $setting) {thrownewException('Active plan id not found.'); } $activePlanId = $setting->value; $agreement =newAgreement(); $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 =newPlan(); $plan->setId($activePlanId); $agreement->setPlan($plan);// Add Payer $payer =newPayer(); $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();returnredirect($approvalUrl); }/** * Get list of all webhooks * * @return\PayPal\Api\WebhookList */publicfunctionlistAllWebhooks() { $output =Webhook::getAllWithParams([], $this->apiContext);return $output; }/** * Delete all webhooks * * @returnbool */publicfunctiondeleteAllWebhooks() { $webhookList =$this->listAllWebhooks();foreach ($webhookList->getWebhooks() as $webhook) { $webhook->delete($this->apiContext); }returntrue; }/** * Register primary webhooks * * @returnWebhook|string */publicfunctionregisterPrimaryWebhooks() { $setting =Setting::where('name', '=', 'primary_webhooks_registered')->first();if ($setting) {return'primary webhooks already registered'; } $baseUrl =env('APP_URL'); $webhook =newWebhook(); $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 =newSetting( ['name'=>'primary_webhooks_registered','value'=> $output->getId(), ] ); $newSetting->save();return $output; }publicfunctionvalidatePrimaryWebhooks() { $setting =Setting::where('name', '=', 'primary_webhooks_registered')->first();if (! $setting) {thrownewException('Primary webhooks not registered.'); } $webhookId = $setting->value; $requestBody =file_get_contents('php://input'); $headers =getallheaders(); $headers =array_change_key_case($headers,CASE_UPPER); $signatureVerification =newVerifyWebhookSignature(); $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(); (newSetting( ['name'=>'webhook '.date('Y-m-d H:i:s'),'value'=> $status . $requestBody, ] ))->save();returnresponse()->json(['status'=>'OK' ]); }}
Here is the code. I’ll explain the four main methods in the class. First, ‘activatePlan’ – this will create the billing plan and activate it. Second, ‘registerPrimaryWebhooks’ – which will register some webhooks with PayPal. Third, ‘validatePrimaryWebhooks’ – which will validate/handle the primary webhooks, when one is received. And last, ‘createBillingAgreement’ – which will create a billing agreement and return a redirect to the PayPal’s approval link for that agreement.
Here is also my ‘PaypalController’ which handles the requests and also has some PayPal logic to execute the billing agreement once the user is redirected to the success url after approval.
<?phpnamespaceApp\Http\Controllers;useApp\Setting;useApp\Logic\Paypal\Paypal;useIlluminate\Http\Request;usePayPal\Api\Agreement;classPaypalControllerextendsController{publicfunctioncreateAndActivatePlan() { $setting =Setting::where('name', '=', 'active_plan_id')--->first();if ($setting) {return'plan already activated'; } $paypal =newPaypal(); $plan = $paypal->activatePlan();if ($plan) {return'success'; } else {return'failure'; } }publicfunctionredirectToPaypal() { $paypal =newPaypal();return $paypal->createBillingAgreement(); }publicfunctionexecuteAgreement() {// ## Approval Status// Determine if the user accepted or denied the requestif (isset($_GET['success']) && $_GET['success'] =='true') { $paypal =newPaypal(); $token = $_GET['token']; $agreement =newAgreement();// ## Execute Agreement// Execute the agreement by passing in the token $agreement->execute($token, $paypal->apiContext);// ## Get Agreement// Make a get call to retrieve the executed agreement details $agreement =Agreement::get($agreement->getId(), $paypal->apiContext);dd($agreement); } }publicfunctionregisterPrimaryWebhooks() { $paypal =newPaypal();return $paypal->registerPrimaryWebhooks(); }publicfunctionhandlePrimaryWebhooks() { $paypal =newPaypal();return $paypal->validatePrimaryWebhooks(); }}