In my Laravel application I have a page where users must pay £150 for a membership fee. To process this payment I chose Stripe.
I store all of the charges in a payments table, along with a user's ID.
Payments table
Schema::create('payments', function (Blueprint $table) {
$table->increments('id');
$table->uuid('user_id');
$table->string('transaction_id');
$table->string('description');
$table->string('amount');
$table->string('currency');
$table->datetime('date_recorded');
$table->string('card_brand');
$table->string('card_last_4', 4);
$table->string('status');
$table->timestamps();
});
I also implemented a voucher system of my own as I am not using subscriptions.
Voucher table
Schema::create('vouchers', function (Blueprint $table) {
$table->increments('id');
$table->string('code');
$table->integer('discount_percent');
$table->dateTime('expires_on');
$table->timestamps();
});
Payment Controller
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Carbon\Carbon;
use App\User;
use App\Payment;
use App\Voucher;
use App\Mail\User\PaymentReceipt;
use App\Mail\Admin\UserMembershipPaid;
use Log;
use Mail;
use Validator;
use Stripe;
use Stripe\Error\Card;
class PaymentController extends Controller
{
/**
* Set an initial amount to be used by the controller
*
* #var float
*/
private $amount = 150.00;
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('verified');
$this->middleware('is_investor');
$this->middleware('is_passive_member');
}
/**
* Display a form allowing a user to make a payment
*
* #return void
*/
public function showPaymentForm()
{
return view('user.payment');
}
/**
* Handle an entered voucher code by the user
* Either calculate a discount or skip the payment form
*
* #param [type] $request
* #return void
*/
public function processVoucher(Request $request)
{
$rules = [
'code' => 'required|exists:vouchers',
];
$messages = [
'code.required' => 'You submitted a blank field',
'code.exists' => 'This voucher code is not valid'
];
Validator::make($request->all(), $rules, $messages)->validate();
$entered_voucher_code = $request->get('code');
$voucher = Voucher::where('code', $entered_voucher_code)->where('expires_on', '>', Carbon::now())->first();
// If the voucher exists
if ($voucher) {
$discount_percent = $voucher->discount_percent;
$new_amount = $this->amount - ($discount_percent / 100 * $this->amount);
// As Stripe won't handle charges of 0, we need some extra logic
if ($new_amount <= 0.05) {
$this->upgradeAccount(auth()->user());
Log::info(auth()->user()->log_reference . " used voucher code {$voucher->code} to get a 100% discount on their Active membership");
return redirect()->route('user.dashboard')->withSuccess("Your membership has been upgraded free of charge.");
}
// Apply the discount to this session
else {
Log::info(auth()->user()->log_reference . " used voucher code {$voucher->code} to get a {$voucher->discount_percent}% discount on their Active membership");
// Store some data in the session and redirect
session(['voucher_discount' => $voucher->discount_percent]);
session(['new_price' => $this->amount - ($voucher->discount_percent / 100) * $this->amount]);
return redirect()->back()->withSuccess([
'voucher' => [
'message' => 'Voucher code ' . $voucher->code . ' has been applied. Please fill in the payment form',
'new_price' => $new_amount
]
]);
}
}
// Voucher has expired
else {
return redirect()->back()->withError('This voucher code has expired.');
}
}
/**
* Handle a Stripe payment attempt from the Stripe Elements form
* Takes into account voucher codes if they are less than 100%
*
* #param Request $request
* #return void
*/
public function handleStripePayment(Request $request)
{
// Retreive the currently authenticated user
$user = auth()->user();
// Get the Stripe token from the request
$token = $request->get('stripeToken');
// Set the currency for your country
$currency = 'GBP';
// Set an initial amount for Stripe to use with the charge
$amount = $this->amount;
// A description for this payment
$description = "Newable Private Investing Portal - Active Membership fee";
// Initialize Stripe with given public key
$stripe = Stripe::make(config('services.stripe.secret'));
// Attempt a charge via Stripe
try {
Log::info("{$user->log_reference} attempted to upgrade their membership to Active");
// Check that token was sent across, if it wasn't, stop
if (empty($token)) {
return redirect()->back()->withErrors([
'error' => "Token error, do you have JavaScript disabled?"
]);
}
// Check whether a discount should be applied to this charge
if (session()->has('voucher_discount')) {
$discount_percentage = session()->pull('voucher_discount');
$discount = ($discount_percentage / 100) * $amount;
$amount = $amount - $discount;
session()->forget('new_price');
}
// Create a charge with an idempotent id to prevent duplicate charges
$charge = $stripe->idempotent(session()->getId())->charges()->create([
'amount' => $amount,
'currency' => $currency,
'card' => $token,
'description' => $description,
'statement_descriptor' => 'Newable Ventures',
'receipt_email' => $user->email
]);
//If the payment is successful, store the payment, send some emails and upgrade this user
if ($charge['status'] == 'succeeded') {
$this->storePayment($charge);
Mail::send(new PaymentReceipt($user));
Mail::send(new UserMembershipPaid($user));
$this->upgradeAccount($user);
return redirect()->route('user.dashboard')->withSuccess("Your payment was successful, you will soon recieve an email receipt.");
// If the payment was unsuccessful
} else {
$this->storePayment($charge);
Log::error("Stripe charge failed for {$user->log_reference}");
return redirect()->back()->withErrors([
'error' => "Unfortunately, your payment was unsuccessful."
]);
}
} catch (Exception $e) {
Log::error("Error attempting Stripe Charge for {$user->log_reference} - Exception - error details {$e->getMessage()}");
return redirect()->back()->withErrors([
'error' => $e->getMessage()
]);
} catch (\Cartalyst\Stripe\Exception\MissingParameterException $e) {
Log::error("Error attempting Stripe Charge for {$user->log_reference} - MissingParameterException - error details {$e->getMessage()}");
return redirect()->back()->withErrors([
'error' => $e->getMessage()
]);
} catch (\Cartalyst\Stripe\Exception\CardErrorException $e) {
Log::error("Error attempting Stripe Charge for {$user->log_reference} - CardErrorException - error details {$e->getMessage()}");
return redirect()->back()->withErrors([
'error' => $e->getMessage()
]);
} catch (\Cartalyst\Stripe\Exception\ApiLimitExceededException $e) {
Log::error("Error attempting Stripe Charge for {$user->log_reference} - ApiLimitExceededException - error details {$e->getMessage()}");
return redirect()->back()->withErrors([
'error' => $e->getMessage()
]);
} catch (\Cartalyst\Stripe\Exception\BadRequestException $e) {
Log::error("Error attempting Stripe Charge for {$user->log_reference} - BadRequestException - error details {$e->getMessage()}");
return redirect()->back()->withErrors([
'error' => $e->getMessage()
]);
} catch (\Cartalyst\Stripe\Exception\ServerErrorException $e) {
Log::error("Error attempting Stripe Charge for {$user->log_reference} - ServerErrorException - error details: {$e->getMessage()}");
return redirect()->back()->withErrors([
'error' => $e->getMessage()
]);
} catch (\Cartalyst\Stripe\Exception\UnauthorizedException $e) {
Log::error("Error attempting Stripe Charge for {$user->log_reference} - UnauthorizedException - error details: {$e->getMessage()}");
return redirect()->back()->withErrors([
'error' => $e->getMessage()
]);
}
}
/**
* Store a Stripe chargee in our database so we can reference it later if necessary
* Charges stored against users for cross referencing and easy refunds
*
* #return void
*/
private function storePayment(array $charge)
{
$payment = new Payment();
$payment->transaction_id = $charge['id'];
$payment->description = $charge['description'];
$payment->amount = $charge['amount'];
$payment->currency = $charge['currency'];
$payment->date_recorded = Carbon::createFromTimestamp($charge['created']);
$payment->card_brand = $charge['source']['brand'];
$payment->card_last_4 = $charge['source']['last4'];
$payment->status = $charge['status'];
auth()->user()->payments()->save($payment);
if ($payment->status === "succeeded") {
Log::info("Successful Stripe Charge recorded for {$user->log_reference} with Stripe reference {$payment->transaction_id} using card ending {$payment->card_last_4}");
} else {
Log::info("Failed Stripe Charge recorded for {$user->log_reference} with Stripe reference {$payment->transaction_id} using card ending {$payment->card_last_4}");
}
}
/**
* Handle a user account upgrade from whatever to Active
*
* #param User $user
* #return void
*/
private function upgradeAccount(User $user)
{
$current_membership_type = $user->member_type;
$user->member_type = "Active";
$user->save();
Log::info("{$user->log_reference} has been upgraded from a {$current_membership_type} member to an Active Member.");
}
}
processVoucher() takes a string entered by the user, checks to see if it exists in the vouchers table and then applies the discount percentage to the fee of 150.00.
It then adds the new value to the session and I use that in the Stripe Charge.
The issue
The issue is that Stripe's minimum chargable amount is 0.05, so to circumvent this issue I've just called a method that upgrades the account.
I should, in theory, store the free upgrades in the charges table but I would end up with multiple null values.
Is this a horrible solution?
In the User model I also have the following methods:
/**
* Relationship to payments
*/
public function payments()
{
return $this->hasMany(Payment::class, 'user_id', 'id');
}
/**
* Relationship to payments to get most recent payment
*
* #return void
*/
public function latest_payment()
{
return $this->hasOne(Payment::class, 'user_id', 'id')->latest();
}
These are used so I can calculate when a user last made a payment, as I needed to bill them annually without using subscriptions as users can also use 100% off vouchers to upgrade.
I made this console command:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Carbon\Carbon;
use App\User;
use App\Payment;
use Log;
class ExpireMembership extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'membership:expire';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Expire user memberships after 1 year of being Active.';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
//Retrieve all users who are an active member with their list of payments
$activeUsers = User::where('member_type', 'Active')->get();
//Get current date
$current_date = Carbon::now();
foreach($activeUsers as $user){
$this->info("Checking user {$user->log_reference}");
// If a user has at least one payment recorded
if($user->payments()->exists()){
//Get membership end date (latest payment + 1 year added)
$membership_end_date = $user->payments
->where('description', 'Newable Private Investing Portal - Active Membership fee')
->sortByDesc('created_at')
->first()->created_at->addYear();
}
// If the user has no payments but is an active member just check if they're older than a year
else{
$membership_end_date = $user->created_at->addYear();
}
//If the membership has gone over 1 year, expire the membership.
if ($current_date->lessThanOrEqualTo($membership_end_date)) {
$user->member_type = "Passive";
$user->save();
$this->info($user->log_reference . "membership has expired and membership status has been set to Passive.");
Log::info($user->log_reference . "membership has expired and membership status has been set to Passive.");
}
}
$this->info("Finished checking user memberships.");
}
}
Users who use vouchers do not have payments, so figuring out when to bill them automatically is tricky.
Related
I'm using the mollie developer setup to simulate payment offers. I've followed a tutorial by my teacher and he hasn't any problems. So what is going on?
Because Mollie is an online service, I'm using Ngrok to create a tunnel for the webhook and my localhost. I'll post my code below but know that I wrote a Log which gave as response:
[2022-07-26 18:22:54] local.ERROR: Method App\Http\Controllers\webHookController::handle does not exist. {"exception":"[object] (BadMethodCallException(code: 0): Method App\Http\Controllers\webHookController::handle does not exist. at C:\Users\stefv\SCHOOL\GENT\NMD\WEBDEV2\werkstuk---geboortelijst-Stef-Verniers\vendor\laravel\framework\src\Illuminate\Routing\Controller.php:68)
I have no clue what exact error this log is targeting so if anyone can point this out, it's much appreciated!
Because the webhook can't get to mollie my status inside my database can't be changed so it's on 'pending' forever...
So I'm looking for a way to fix the error so my webhook can reach Mollie and my payment is accepted so the payment status in my database can change to 'paid'.
This is my code:
My Controller which sets up Mollie:
public function additional(Request $r)
{
$articles = Article::all();
$categories = Category::all();
$websites = Website::all();
$session_id = request()->session()->getId();
$cartItems = Cart::session($session_id)->getContent();
$cartTotal = Cart::session($session_id)->getTotal();
$r->session()->put('cusnaam', 'Cas');
$r->session()->put('tel', 'Tel');
$r->session()->put('email', 'Email');
$r->session()->put('pb', 'pb');
return view('customer-info', compact('articles', 'categories', 'websites', 'cartItems', 'cartTotal'));
}
public function checkout(Request $r)
{
$session_id = request()->session()->getId();
$cartTotal = Cart::session($session_id)->getTotal();
$order = new Order();
$order->name = $r->input('Cus');
$order->note = $r->input('pb');
$order->total = $cartTotal;
$order->status = 'pending';
$order->save();
$mollie = new \Mollie\Api\MollieApiClient();
$mollie->setApiKey("test_6vGchNb62gynePtcsNsbm8dartsmjU");
$mollie->methods->allAvailable();
$session_id = request()->session()->getId();
$cartItems = Cart::session($session_id)->getContent();
$valuta = number_format($cartTotal, 2);
$webhookUrl = route('webhooks.mollie');
if(App::environment('local')) {
$webhookUrl = 'https://5d25-84-199-205-243.eu.ngrok.io/webhooks/mollie';
};
$payment = Mollie::api()->payments->create([
"amount" => [
"currency" => "EUR",
"value" => $valuta // You must send the correct number of decimals, thus we enforce the use of strings
],
"description" => "Bestelling op dag " . date('d-m-Y h:i'),
"redirectUrl" => route('success'),
"webhookUrl" => $webhookUrl,
"metadata" => [
"order_id" => $order->id,
"order_name" => $order->name
],
]);
return redirect($payment->getCheckoutUrl(), 303);
}
public function success()
{
return view('succes');
}
And this is the controller that handles the webhook:
public function handleWebhookNotification(Request $request)
{
$payment = Mollie::api()->payments->get($request->id);
$orderId = $payment->metadata->order_id;
if ($payment->isPaid() && ! $payment->hasRefunds() && ! $payment->hasChargebacks()) {
$order = Order::findOrFail($orderId);
$order->status = 'paid';
$order->save();
Log::alert('tis in de cachoche');
} elseif ($payment->isOpen()) {
/*
* The payment is open.
*/
} elseif ($payment->isPending()) {
/*
* The payment is pending.
*/
} elseif ($payment->isFailed()) {
/*
* The payment has failed.
*/
} elseif ($payment->isExpired()) {
/*
* The payment is expired.
*/
} elseif ($payment->isCanceled()) {
/*
* The payment has been canceled.
*/
} elseif ($payment->hasRefunds()) {
/*
* The payment has been (partially) refunded.
* The status of the payment is still "paid"
*/
} elseif ($payment->hasChargebacks()) {
/*
* The payment has been (partially) charged back.
* The status of the payment is still "paid"
*/
}
}
I'm trying to register a credit card with MangoPay.
I've installed the mangopay/php-sdk-v2 package.
To register a credit card, it needs three steps.
Create a token of the card
Post card info (using a url created by the token) that will render a string that start with data=
Add the registered card to the MangoPay user
// ProfilController.php
/**
* #Route("/payment/{id}", name="payment")
* * #param int $id
*/
public function payment(Request $request, ApiUser $ApiUser, $id): Response
{
$returnUrl = "";
$user = $this->userRepository->findOneBy(['id' => $id]);
$userId = $user->getIdMangopay();
$registration = $ApiUser->Registration($userId);
if($request->request->count() > 0){
$payment = new PaymentMethod();
$payment->setName($request->request->get('name'));
$payment->setCardNumber($request->request->get('cardNumber'));
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($payment);
$entityManager->flush();
$registrationCard = $ApiUser->RegistrationCard($registration, $request);
$returnUrl = 'http' . (isset($_SERVER['HTTPS']) ? 's' : '') . '://' . $_SERVER['HTTP_HOST'];
$returnUrl .= '/profil';
}
return $this->render('home/payment.html.twig', [
'CardRegistrationUrl' => $registration->CardRegistrationURL,
'Data' => $registration->PreregistrationData,
'AccessKeyRef' => $registration->AccessKey,
'returnUrl' => $returnUrl,
]);
}
The Registration and ResitrationCard functions come from the ApiUser file:
// ApiUser.php
public function Registration($UserId)
{
$CardRegistration = new \MangoPay\CardRegistration();
$CardRegistration->UserId = $UserId;
$CardRegistration->Currency = "EUR";
$CardRegistration->CardType = "CB_VISA_MASTERCARD";
$Result = $this->mangoPayApi->CardRegistrations->Create($CardRegistration);
$this->registrationInfo = $Result;
$this->CardRegistrationUrl = $Result->CardRegistrationURL;
return $Result;
}
public function RegistrationCard($CardInfo)
{
$cardRegister = $this->mangoPayApi->CardRegistrations->Get($CardInfo->Id);
$cardRegister->RegistrationData = $_SERVER['QUERY'];
$updatedCardRegister = $this->mangoPayApi->CardRegistrations->Update($cardRegister);
return $Result;
}
I'm able to create the token of the card and get the data= string, but the problem is that I cannot do the last step.
It seems that I cannot enter into the if statement, so it doesn't register the card on the database and I cannot update the card information (3rd step).
The returnUrl, I can simply put it outside of the if statement to make it works, but I want to change it only if the form is valid.
How can I fix the statement? Why doesn't it enter into the if?
Please try to use the regular form validation process of Symfony, and let me know if this helps.
To do this, you need to customize the input name attribute for it to match the payment API config.
In your Type class:
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
// ...
->add('new-input-name-goes-here', TextType::class, [
'property_path' => '[data]'
]);
}
public function getBlockPrefix()
{
return '';
}
I'm getting this error when i try to register via google api
string(331) "Legacy People API has not been used in project ******* before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/legacypeople.googleapis.com/overview?project=******** then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry."
And when i go that url i'm receiving
Failed to load.
There was an error while loading /apis/api/legacypeople.googleapis.com/overview?project=******&dcccrf=1. Please try again.
My google.php code in /vendor/league/oauth2-google/src/Provider is
<?php
namespace League\OAuth2\Client\Provider;
use League\OAuth2\Client\Exception\HostedDomainException;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use League\OAuth2\Client\Token\AccessToken;
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
use Psr\Http\Message\ResponseInterface;
class Google extends AbstractProvider
{
use BearerAuthorizationTrait;
const ACCESS_TOKEN_RESOURCE_OWNER_ID = 'id';
/**
* #var string If set, this will be sent to google as the "access_type" parameter.
* #link https://developers.google.com/accounts/docs/OAuth2WebServer#offline
*/
protected $accessType;
/**
* #var string If set, this will be sent to google as the "hd" parameter.
* #link https://developers.google.com/accounts/docs/OAuth2Login#hd-param
*/
protected $hostedDomain;
/**
* #var array Default fields to be requested from the user profile.
* #link https://developers.google.com/+/web/api/rest/latest/people
*/
protected $defaultUserFields = [
'id',
'name(familyName,givenName)',
'displayName',
'emails/value',
'image/url',
];
/**
* #var array Additional fields to be requested from the user profile.
* If set, these values will be included with the defaults.
*/
protected $userFields = [];
/**
* Use OpenID Connect endpoints for getting the user info/resource owner
* #var bool
*/
protected $useOidcMode = false;
public function getBaseAuthorizationUrl()
{
return 'https://accounts.google.com/o/oauth2/auth';
}
public function getBaseAccessTokenUrl(array $params)
{
return 'https://www.googleapis.com/oauth2/v4/token';
}
public function getResourceOwnerDetailsUrl(AccessToken $token)
{
if ($this->useOidcMode) {
// OIDC endpoints can be found https://accounts.google.com/.well-known/openid-configuration
return 'https://www.googleapis.com/oauth2/v3/userinfo';
}
// fields that are required based on other configuration options
$configurationUserFields = [];
if (isset($this->hostedDomain)) {
$configurationUserFields[] = 'domain';
}
$fields = array_merge($this->defaultUserFields, $this->userFields, $configurationUserFields);
return 'https://www.googleapis.com/plus/v1/people/me?' . http_build_query([
'fields' => implode(',', $fields),
'alt' => 'json',
]);
}
protected function getAuthorizationParameters(array $options)
{
$params = array_merge(
parent::getAuthorizationParameters($options),
array_filter([
'hd' => $this->hostedDomain,
'access_type' => $this->accessType,
// if the user is logged in with more than one account ask which one to use for the login!
'authuser' => '-1'
])
);
return $params;
}
protected function getDefaultScopes()
{
return [
'email',
'openid',
'profile',
];
}
protected function getScopeSeparator()
{
return ' ';
}
protected function checkResponse(ResponseInterface $response, $data)
{
if (!empty($data['error'])) {
$code = 0;
$error = $data['error'];
if (is_array($error)) {
$code = $error['code'];
$error = $error['message'];
}
throw new IdentityProviderException($error, $code, $data);
}
}
protected function createResourceOwner(array $response, AccessToken $token)
{
$user = new GoogleUser($response);
// Validate hosted domain incase the user edited the initial authorization code grant request
if ($this->hostedDomain === '*') {
if (empty($user->getHostedDomain())) {
throw HostedDomainException::notMatchingDomain($this->hostedDomain);
}
} elseif (!empty($this->hostedDomain) && $this->hostedDomain !== $user->getHostedDomain()) {
throw HostedDomainException::notMatchingDomain($this->hostedDomain);
}
return $user;
}
}
How to fix this issue?
Legacy People API has not been used in project ******* before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/legacypeople.googleapis.com/overview?project=********
As the error message states you have not enabled the people api in your project and as you have included email and profile and are trying to request profiled data about the user.
return 'https://www.googleapis.com/plus/v1/people/me?' . http_build_query([
'fields' => implode(',', $fields),
'alt' => 'json',
You need to enable the people api in our project before you can request data. Click the link and follow the instructions below.
Go to Google developer console click library on the left. Then search for the API you are looking to use and click enable button
Wait a couple of minutes then run your code again. Then you will be able to make requests to the people api.
return 'https://www.googleapis.com/plus/v1/people/me?' . http_build_query([
'fields' => implode(',', $fields),
'alt' => 'json',
Legacy endpoint:
I also recommend up update your endpoint to the new people.get endpoint
https://people.googleapis.com/v1/people/me
I have this controller that is supposed to perform PayPal payments. The payment function is working well but on getting to success function I am getting an error Illegal string offset 'total' . I am passing $this->productData($request) as suggested in this question. I tried creating a variable $total = $response['AMT'] which is the response from setCheckoutDetails but I still got the same error. How do I go about it?
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Srmklive\PayPal\Services\ExpressCheckout;
class PayPalController extends Controller
{
private function projectData(Request $request){
// dd($request->all());
$item = [];
$datat = array_map(function($item){
return [
'name'=>$request->project_id,
'price'=>$request->budget,
'desc'=>'Deposit',
'qty'=>1
];
}, $item);
$data = [
'items'=>$datat,
'invoice_id' => uniqid(),
'invoice_description' => "Payment for Project No.".$request->project_id." Amount ".$request->budget,
'return_url' => route('payment.success'),
'cancel_url' => route('payment.cancel'),
'total'=>$request->budget
];
// dd($data);
return $data;
}
/**
* Responds with a welcome message with instructions
*
* #return \Illuminate\Http\Response
*/
public function payment(Request $request) {
$data = $this->projectData($request);
$provider = new ExpressCheckout;
$response = $provider->setExpressCheckout($data);
// dd($response);
// $response = $provider->setExpressCheckout($data, true);
return redirect($response['paypal_link']);
}
/**
* Responds with a welcome message with instructions
*
* #return \Illuminate\Http\Response
*/
public function cancel()
{
dd('Your payment is canceled. You can create cancel page here.');
}
/**
* Responds with a welcome message with instructions
*
* #return \Illuminate\Http\Response
*/
public function success(Request $request)
{
$provider = new ExpressCheckout;
$response = $provider->getExpressCheckoutDetails($request->token);
$token = $response['TOKEN'];
$payerId = $response['PAYERID'];
$total = $response['AMT'];
// dd($response);
if (in_array(strtoupper($response['ACK']), ['SUCCESS', 'SUCCESSWITHWARNING'])) {
// dd('Payment successful');
//Performing transaction
$payment_status = $provider->doExpressCheckoutPayment($token, $payerId, $this->projectData($request));
dd($payment_status);
}
dd('Something is wrong.');
}
}
You have to pass three parameters
data, token, PAYERID
Data can service information like
$data = array(
'total' => Total amount,
'invoice_id' => Invoicen number,
'invoice_description' => invoice descrption
);
And items as well which will contain name, price, desc and qty
I am intergrating paypal payment gateway on my application, before user first leave my site for paypal, I saved all the necessary information, and set payment_status to be 0, when user returns, if payment is sucessful, it returns to a new page where it gets the paymentId, and payerID, plus sets the paymentstatus to 1.
the thing is that if i set the database value for paymentID and PaymentId to null, it doesnt save.
if i dont set it to anything i.e leave it blank, it would save, but retruns an error:
Integrity constraint violation: 1048 Column 'PayerID' cannot be null (SQL: update `paypal_officers` set `paymentId` = , `PayerID` = , `updated_at` = 2018-07-15 07:29:21 where `id` = 1)
i cant seem to figure out what the problem is.
Here is the method that does the saving.
public function getPaymentStatus(Input $input)
{
/** Get the payment ID before session clear **/
$payment_id = Session::get('paypal_payment_id');
$insert_the_user_id = PaypalOfficer::findorFail(\session()->get('receivers_main_info_id'));
$insert_the_user_id->update([
'paymentId' => Input::get('paymentId'),
'PayerID' => Input::get('PayerID'),
'payment_status' => 1,
]);
$insert_the_user_id->save();
/** clear the session payment ID **/
Session::forget('paypal_payment_id');
if (empty(Input::get('PayerID')) || empty(Input::get('token'))) {
\Session::put('error', 'Payment failed');
return Redirect::to('account/send-money/paypal/send')->with('payer_id', $payment_id);;
}
$payment = Payment::get($payment_id, $this->_api_context);
$execution = new PaymentExecution();
$execution->setPayerId(Input::get('PayerID'));
/**Execute the payment **/
$result = $payment->execute($execution, $this->_api_context);
if ($result->getState() == 'approved') {
\Session::put('success', 'Payment success');
return Redirect::to('account/send-money/paypal/stagged/paypal')->with('payer_id', $result);
}
\Session::put('error', 'Payment failed');
return Redirect::to('account/send-money/paypal/send');
}
My Model
namespace App;
use Illuminate\Database\Eloquent\Model;
class PaypalOfficer extends Model
{
protected $fillable = [
'payment_status',
'paymentId',
'PayerID',
'user_id',
'amount',
'destination_account_number',
'destination_account_name',
'destination_bank_name',
'destination_phone_number',
'destination_country',
'destination_state',
'currency_symbol',
'receivers_name',
];
//
public function user(){
return $this->belongsTo(User::class);
}
}
Please what must i be missing in the code , anybody please!
Error occurs when Input::get('PayerID') is empty or null. You must be firstly check all required field exists in input, then try to update PaypalOfficer. Also in eloquent model use ->update method that case ->save does not need. Fix this part
public function getPaymentStatus(Input $input)
{
// firstly check PayerID and paymentId input exist
if (empty(Input::get('PayerID')) || empty(Input::get('paymentId')) || empty(Input::get('token'))) {
\Session::put('error', 'Payment failed');
return Redirect::to('account/send-money/paypal/send')->with('payer_id', $payment_id):
}
/** Get the payment ID before session clear **/
$payment_id = Session::get('paypal_payment_id');
$insert_the_user_id = PaypalOfficer::findorFail(\session()->get('receivers_main_info_id'));
$insert_the_user_id->update([
'paymentId' => Input::get('paymentId'),
'PayerID' => Input::get('PayerID'),
'payment_status' => 1,
]);
/** clear the session payment ID **/
Session::forget('paypal_payment_id');