Whenever setTestMode() method is used, the error "Security header is not valid" pops up.
But if I remove setTestMode() and just keep the setUsername(), setPassword(), and setSignature() methods, it goes through and redirects straight to PayPal (Live Paypal).
So AFAIK the problem should lie in how I'm using setTestMode and not about incorrect API creds as most "Security header is not valid" errors are about.
I am currently using Laravel 5.8 with Omnipay/PayPal using PayPal Express Checkout
Here are the files that were used
Paypal.php
public function gateway()
{
$gateway = Omnipay::create('PayPal_Express');
$gateway->setUsername(config('services.paypal.username'));
$gateway->setPassword(config('services.paypal.password'));
$gateway->setSignature(config('services.paypal.signature'));
$gateway->setTestMode(config('services.paypal.sandbox'));
// $gateway->setTestMode(true);
return $gateway;
}
public function purchase(array $parameters)
{
$response = $this->gateway()
->purchase($parameters)
->send();
return $response;
}
PaypalController.php
public function checkout($order_id)
{
$order = Order::findOrFail(decrypt($order_id));
$paypal = new PayPal;
$response = $paypal->purchase([
'amount' => $paypal->formatAmount($order->amount),
'transactionId' => $order->transaction_id,
'currency' => 'PHP',
'cancelUrl' => $paypal->getCancelUrl($order),
'returnUrl' => $paypal->getReturnUrl($order),
'notifyUrl' => $paypal->getNotifyUrl($order),
]);
if ($response->isRedirect()) {
$response->redirect();
}
return redirect()->back()->with([
'message' => $response->getMessage(),
]);
}
Here are the contents of the $response
ExpressAuthorizeResponse {#1098 ▼
#liveCheckoutEndpoint: "https://www.paypal.com/cgi-bin/webscr"
#testCheckoutEndpoint: "https://www.sandbox.paypal.com/cgi-bin/webscr"
#request: ExpressAuthorizeRequest {#1095 ▼
#liveEndpoint: "https://api-3t.paypal.com/nvp"
#testEndpoint: "https://api-3t.sandbox.paypal.com/nvp"
#negativeAmountAllowed: true
#parameters: ParameterBag {#1097 ▶}
#httpClient: Client {#1063 ▶}
#httpRequest: Request {#1086 ▶}
#response: ExpressAuthorizeResponse {#1098}
#currencies: ISOCurrencies {#1096}
#zeroAmountAllowed: true
}
#data: array:9 [▼
"TIMESTAMP" => "2020-02-03T11:04:45Z"
"CORRELATIONID" => "c8d066c9b5ccd"
"ACK" => "Failure"
"VERSION" => "119.0"
"BUILD" => "54118205"
"L_ERRORCODE0" => "10002"
"L_SHORTMESSAGE0" => "Security error"
"L_LONGMESSAGE0" => "Security header is not valid"
"L_SEVERITYCODE0" => "Error"
]
}
---EDIT---
Just made a new Sandbox Business Account and used that one's NVP API Creds and then it worked! The old account's problem was probably it's email that I edited because that was the only difference between the old and new one.
The live and test/sandbox environments are completely separate, and so require separate credentials.
For the PayPal Sandbox (test mode), you need an API Username, Password, and Signature from a sandbox PayPal Business account, via https://www.paypal.com/signin?intent=developer&returnUri=https%3A%2F%2Fdeveloper.paypal.com%2Fdeveloper%2Faccounts%2F
The 10002 "Security Header is not Valid" always indicates your credentials are not valid, i.e. there is a problem with the Username/Password/Signature or you are using live ones in sandbox mode, or vice-versa
Related
I've been trying to get the saved customer id for stripe from database but with no luck.
It works everywhere else, I could get it and save it again if I wanted, but whenever I try to use it in payment intent to automatically renew a subscription, it gives me this error: Trying to get property 'stripecustomerid' of non-object.
this is the bit of the stripe code for recurring charge where the error happens:
public function renew($subscription)
{
\Stripe\Stripe::setApiKey('sk_test_XXXXXXXX');
header('Content-Type: application/json');
try {
$json_str = file_get_contents('php://input');
$json_obj = json_decode($json_str);
$user = \Auth::user();
$payment_methods = \Stripe\PaymentMethod::all([
'customer' => $user->stripecustomerid,
'type' => 'card'
]);
$payment_intent = \Stripe\PaymentIntent::create([
'amount' => $subscription->plan->stripePrice(),
'currency' => 'usd',
'customer' => $user->stripecustomerid,
'payment_method' => $payment_methods->data[0]->id,
'off_session' => true,
'confirm' => true,
]);
echo json_encode([
'paymentIntent' => $payment_intent,
]);
}
catch (\Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
}
and stripecustomerid is the name of the column where I saved the customer id.
I can print it in another function, and it works when I use GET, but it just doesn't work when the subscription tries to renew.
I try to implement Paypal subscription service according to: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_create
This is my first try.
In sandbox business account I have created two test subscriptions: monthly and yearly and configured application with their id's.
This is the method:
public function createSubscription($planSlug, $name, $email) {
return $this->makeRequest(
'POST',
'v1/billing/subscriptions',
[],
[
'plan_id' => $this->plans[$planSlug],
'subscriber' => [
'name' => [
'given_name' => $name,
],
'email_address' => $email
],
'application_context'=> [
'brand_name' => config('app.name'),
'shipping_preference' => 'NO_SHIPPING',
'user_action' => 'SUBSCRIBE_NOW',
'return_url' => route('subscribe.approval', ['plan' => $planSlug]),
'cancel_url'=> route('subscribe.cancelled')
],
],
[],
$isJsonRequest = true
);
}
However, when I make a call to API, to create a test subscription, I get weird response that 'name' parameter is formed incorrectly:
php artisan tinker
>>> $paypal = resolve(App\Services\PaypalService::class);
=> App\Services\PaypalService {#3413}
>>> $paypal->createSubscription('monthly', 'Test', 'test#test.com');
GuzzleHttp\Exception\ClientException with message 'Client error: `POST https://api-
m.sandbox.paypal.com/v1/billing/subscriptions` resulted in a `400 Bad Request` response:
{"name":"INVALID_REQUEST","message":"Request is not well-formed, syntactically incorrect, or
violates schema.","debug_id (truncated...)
This is strange, because in Paypal API doc (see above), the 'name' param is described exactly like that!
Do I miss something or it is Paypal API is acting funky?
try this :
try {
// your code here
} catch(\Throwable $th) {
if ($th instanceof ClientException) {
$r = $th->getResponse();
$responseBodyAsString = json_decode($r->getBody()->getContents());
dd($responseBodyAsString);
}
}
I've faced this too before and it was not easy for me to figure out how to show an explicit error message.
'return_url' => route('subscribe.approval', ['plan' => $planSlug]),
'cancel_url'=> route('subscribe.cancelled')
the problem is in this two url, may be you have changed the APP_URL in the .env
APP_URL=http://127.0.0.1:8000
put app url that and try
Background
I have developed a React app that uses Laravel as an API. I have added logins via Passport and have been using the Personal Access Client approach quite successfully. I can add new users and tokens, I can revoke tokens, I can reset passwords... All API calls (except login and register) are guarded by the API middleware and it works. If I remove the Bearer ${token} from the header on any of these calls it returns 401 unauthenticated due to the ->middleware('auth:api') wrapper.
The Problem
Everything works completely as expected... until I move everything to my Raspberry Pi server. As soon as I moved everything, the problem began. I can login and I can register, but as soon as I use the new bearer token (that I received from my login or register call(s)) on any of the endpoint calls that follow in my flow, it fails with 401 unauthenticated, immediately. I ran the php artisan passport:client --personal command and successfully entered the id and secret into my .env file as usual. I installed all the composer and vendor packages. I installed all passport package(s) and CLI commands.
It only fails on calls that use the auth middleware.
I have done some digging and it seems the only change I can find (significantly) is that the Pi runs a 32 bit PHP where my localhost runs a 64 bit PHP. Other than that its the same code, DB, versions of Laravel and PHP, everything.
I have tried using the command php artisan passport:client --personal --name="app-name" --redirect_uri="http://192.168.1.1/" which puts a record in the "oauth_clients" table but shows the redirect as http://localhost/. I then try to use SQL to change the value of the column named "redirect" to http://localhost/, manually... but again the change does nothing. Calls still return 401 unauthenticated.
The only other things I can find that might be an issue are:
The fact that all tokens in the database table "oauth_access_tokens", under the column called "redirect", are created with the redirect_uri of http://localhost. No matter what I do it's always localhost and not my servers domain or IP address (which is concerning). Manually changing SQL as I said does nothing but I know Laravel uses a few "read-only" columns for auth so I wonder if this is one of them... perhaps personal access tokens only work on localhost?
My email_verified_at column in my "users" table (generated by passport commands) is null because I was not able to setup the "forgot my password" flow of Passport on localhost since emails won't send out on localhost.
What I have setup is this:
public function boot()
{
$this->registerPolicies();
Passport::pruneRevokedTokens();
Passport::tokensExpireIn(Carbon::now()->addDays(1));
Passport::refreshTokensExpireIn(Carbon::now()->addDays(14));
Passport::personalAccessTokensExpireIn(Carbon::now()->addDays(1));
}
AuthServiceProvider Class
public function register(Request $request) {
$validatedData = $request->validate([
'image_url' => 'required',
'last_name' => 'required|max:55',
'image_url' => 'required|max:250',
'first_name' => 'required|max:55',
'password' => 'required|confirmed',
'email' => 'email|required|unique:users',
]);
$validatedData['password'] = bcrypt($request->password);
if ($request->hasFile('image_url')) {
$imageFile = $request->file('image_url');
$imageExtension = $imageFile->extension();
if (strtolower($imageExtension) === 'png' || strtolower($imageExtension) === 'jpg') {
$validatedData['image_url'] = Storage::url( $request->file('image_url')->store('user_pics', 'public') );
}
$user = User::create($validatedData);
date_default_timezone_set('UTC');
$date = new \DateTime( date('Y-m-d H:i:s') );
$user->email_verified_at = $date->format('c');
$accessToken = $user->createToken('authToken-'.$user->id, ['*'])->accessToken;
return response([ 'user' => $user, 'access_token' => $accessToken ]);
} else {
abort(404, 'Cannot register user without a user image!');
}
}
public function login(Request $request) {
$loginData = $request->validate([
'email' => 'email|required',
'password' => 'required'
]);
if (!auth()->attempt($loginData)) {
return response()->json(['statusText' => 'Unauthorized'], 401);
}
$user = auth()->user();
$accessToken = auth()->user()->createToken('authToken-'.$user->id, ['*'])->accessToken;
return response([ 'user' => $user, 'access_token' => $accessToken ]);
}
public function logout(Request $request) {
if (auth()->guard('api')->check()) {
auth()->guard('api')->user()->OauthAcessToken()->delete();
return response()->json([ 'msg' => 'Successfully logged out!' ]);
} else {
return abort(404, 'Must be logged in to log a user out');
}
}
public function refreshToken(Request $request) {
if (auth()->guard('api')->check()) {
$user = auth()->user();
$accessToken = auth()->user()->createToken('authToken-'.$user->id, ['*'])->accessToken;
return response([ 'user' => $user, 'access_token' => $accessToken ]);
} else {
return abort(404, 'Must be logged in to refresh a token!');
}
}
AuthController Class
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users'
],
],
config/Auth.php
APP_NAME=MyName
APP_ENV=dev
APP_DEBUG=true
APP_URL=http://192.168.1.1
PASSPORT_PERSONAL_ACCESS_CLIENT_ID="1"
PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET="[SOME LONG HASH]"
.env File
Finally solved it!!
Turns out it was Apache on the Raspberry Pi server blocking the Authorization header. This finally unblocked me and solved my issues.
For anyone else coming from a Google search, you can go into your /etc/apache2/apache2.conf file and at the very bottom, paste:
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
I am using a Raspberry Pi 4 with 32 bit PHP and Apache2.
Also, I didn't mention in my post that I have been using the following for my apache server root htaccess:
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
.htaccess file, server root
I'm working in laravel 5.4
My transactions are successfull when I try a 'fake_nonce' type of string provided by the braintree docs. But when I tried to get the paymentMethodNonce it always gives me error like nonce not found. And sometimes http error!!! If I try to configure it by myself!
Take a look at my controller function below
public function addOrder(Request $request){
$customer = Braintree_Customer::create([
'firstName' => $request->guest_name,
'email' => $request->guest_email,
'phone' => $request->guest_phone
]);
$customer->success;
$customer->customer->id;
$find = Braintree_Customer::find($customer->customer->id);
$nonceFromTheClient = Braintree_PaymentMethodNonce::find($find);
$result = Braintree_Transaction::sale([
'amount' => $request->subtotal,
'paymentMethodNonce' => $nonceFromTheClient,
'options' => [
'submitForSettlement' => True
]
]);
if ($result->success) {
$settledTransaction = $result->transaction;
} else {
print_r($result->errors);
}
Cart::destroy();
return view('guest/track', compact('result'));
}
$nonceFromTheClient = Braintree_PaymentMethodNonce::find($find);
Your using the wrong nonce, this nonce must come from the DropIn ui and not be generated on your code.
Please check the onPaymentMethodReceived() method provided in the JS SDK.
Please check this reference
I have used this in my project, hoping to integrate paypal express.
but I've got this error:
You do not have permissions to make this API call
here is my controller:
public function index()
{
$gateway = Omnipay::create('PayPal_Express');
$gateway->setUsername('xxxxxx');
$gateway->setPassword('xxxxxx');
$formData = [
'number' => '4242424242424242',
'expiryMonth' => '6',
'expiryYear' => '2016',
'cvv' => '123'
];
$response = $gateway->purchase(
[
'amount' => '1.00',
'currency' => 'USD',
'card' => $formData,
'returnUrl' => '/checkout/',
'cancelUrl' => '/cancel/',
]
)->send();
// Process response
if ($response->isSuccessful()) {
// Payment was successful
print_r($response);
} elseif ($response->isRedirect()) {
// Redirect to offsite payment gateway
$response->redirect();
} else {
// Payment failed
echo $response->getMessage();
}
}
I have enabled api access in my paypal account but got same result. I assume the gateway login details for setUsername and setPassword are the details provided by paypal after setting up the api. I have also changed these details with my paypal login but I've got the same result.