PayPal REST APIs > Create Order > How to add experience_context (e.g. return_url and cancel_url) - rest

Update (Solution):
When the experience_context is added to the request body, the PayPal-Request-Id seems to be become mandatory in the Header of the curl Request.
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
'Authorization: Bearer ' . $this->getAccessToken(),
'PayPal-Request-Id: ' . uniqid()
)
(In PHP) I try to create a simple (sample) order according to the documentation of the REST APIs (see Sample Request)
The request with the following simple data works fine:
{
"intent": "CAPTURE",
"purchase_units": [
{
"amount": {
"currency_code": "EUR",
"value": 100
}
}
]
}
When I try to add the experience_context (https://developer.paypal.com/docs/api/orders/v2/#definition-paypal_wallet_experience_context) according to the Sample Request of the documentation, I get the following answer
The requested URL returned error: 400 Bad Request
{
"intent": "CAPTURE",
"purchase_units": [
{
"amount": {
"currency_code": "EUR",
"value": 100
}
}
],
"payment_source": {
"paypal": {
"experience_context": {
"brand_name": "testmememe",
"cancel_url": "https://example.com/cancelUrl",
"return_url": "https://example.com/returnUrl"
}
}
}
}
What am I doing wrong?
My PayPal-Class:
<?php
class PayPal
{
const CLIENT_ID = 'client_id';
const CLIENT_SECRET = 'client_secret';
const BASE_URL = 'https://api-m.sandbox.paypal.com';
const SESSION_KEY = 'paypal_api';
public function __construct()
{
if(!isset($_SESSION[self::SESSION_KEY])) $_SESSION[self::SESSION_KEY] = array();
}
public function getAccessToken():string
{
if (isset($SESSION[self::SESSION_KEY]['access_token']) && isset($SESSION[self::SESSION_KEY]['access_token_expiration_time']) && $SESSION[self::SESSION_KEY]['access_token_expiration_time'] > time()) return $SESSION[self::SESSION_KEY]['access_token'];
$curl = curl_init();
$options = array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FAILONERROR => true,
CURLOPT_URL => self::BASE_URL . '/v1/oauth2/token',
CURLOPT_HTTPHEADER => array(
'Accept: application/json',
'Accept-Language: en_US'
),
CURLOPT_USERPWD => self::CLIENT_ID . ':' . self::CLIENT_SECRET,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => 'grant_type=client_credentials'
);
curl_setopt_array($curl, $options);
$result = curl_exec($curl);
if (curl_errno($curl))
{
print_r(curl_error($curl));
curl_close($curl);
return '';
}
curl_close($curl);
$data = json_decode($result, true);
if (!isset($data['access_token']) || !isset($data['expires_in'])) return '';
$access_token = $data['access_token'];
$SESSION[self::SESSION_KEY]['access_token'] = $access_token;
$SESSION[self::SESSION_KEY]['access_token_expiration_time'] = time() + $data['expires_in'];
return $access_token;
}
public function createOrder()
{
$amount_obj = new stdClass();
$amount_obj->currency_code = 'EUR';
$amount_obj->value = 100.00;
$object = new stdClass();
$object->amount = $amount_obj;
$experience_context = new stdClass();
$experience_context->brand_name = 'testmememe';
$experience_context->cancel_url = "https://example.com/cancelUrl";
$experience_context->return_url = "https://example.com/returnUrl";
$data = array(
'intent' => 'CAPTURE',
'purchase_units' => array(
$object
),
'payment_source' => array(
'paypal' => array(
'experience_context' => $experience_context
)
)
);
$data_string = json_encode($data);
$curl = curl_init();
$options = array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FAILONERROR => true,
CURLOPT_URL => self::BASE_URL . '/v2/checkout/orders',
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
'Authorization: Bearer ' . $this->getAccessToken()
),
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $data_string
);
curl_setopt_array($curl, $options);
$result = curl_exec($curl);
if (curl_errno($curl))
{
print_r(curl_error($curl));
curl_close($curl);
return '';
}
curl_close($curl);
$data = json_decode($result, true);
if (isset($data['status']) && $data['status'] === 'CREATED' && is_array($data['links']))
{
foreach ($data['links'] as $link)
{
if ($link['rel'] === "approve")
{
$url = $link['href'];
break;
}
}
}
var_dump($data);
echo $url;
}
}
Aufruf:
$paypal_obj = new PayPal;
$paypal_obj->createOrder();

Related

Codeigniter 4 Rest API - 301 Moved Permanently

I have Codeigniter 4 web app that run REST API with firebase/php-jwt on Laragon 5.0.0210523 environment that run Apache-2.4.47, PHP-8.1.7, and MongoDB-4.0.28. I followed a tutorial and it works fine both server REST API and it REST client. After day work, i stop laragon server. In the next day i try run REST API server then tried then run the client but it failed and gave 301 moved permanently error, but i still can access it from postman.
REST API server side
composer.json
***
"require": {
"php": "^7.4 || ^8.0",
"codeigniter4/framework": "^4.0",
"mongodb/mongodb": "^1.12",
"firebase/php-jwt": "^6.3"
},
***
.env file
***
JWT_SECRET_KEY = SomeThing$089
JWT_TIME_TO_LIVE = 3600
app.baseURL = 'http://ci4-api.localhost'
***
Route.php
***
$routes->get('/', 'Home::index');
$routes->resource('api/users');
$routes->post('api/auth', [\App\Controllers\Api\Auth::class, 'index']);
***
JWT_Helper.php
use App\Models\ModelUsers;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
/**
* #throws Exception
*/
function getJWT($authHeader)
{
if (is_null($authHeader)){
throw new Exception("Authentication JWT failed");
}
return explode(" ", $authHeader)[1];
}
function validateJWT($encodedToken)
{
$key = getenv('JWT_SECRET_KEY');
$decodedToken = JWT::decode($encodedToken, new Key($key, 'HS256'));
$modelUsers = new ModelUsers();
$modelUsers->get_email($decodedToken->email);
}
function createJWT($email): string
{
$timeRequest = time();
$timeToken = getenv('JWT_TIME_TO_LIVE');
$timeExpired = $timeRequest + $timeToken;
$payload = [
'email' => $email,
'iat' => $timeRequest,
'exp' => $timeExpired,
];
return JWT::encode($payload, getenv('JWT_SECRET_KEY'), 'HS256');
}
FilterJWT.php
namespace App\Filters;
use CodeIgniter\API\ResponseTrait;
use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Config\Services;
use Exception;
class FilterJWT implements FilterInterface
{
use ResponseTrait;
public function before(RequestInterface $request, $arguments = null)
{
$header = $request->getServer('HTTP_AUTHORIZATION');
try {
helper('jwt');
$encodedToken = getJWT($header);
validateJWT($encodedToken);
return $request;
} catch (Exception $ex) {
return Services::response()->setJSON(
[
'error' => $ex->getMessage(),
]
)->setStatusCode(ResponseInterface::HTTP_UNAUTHORIZED);
}
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
{
// TODO: Implement after() method.
}
}
Filters.php
***
public $aliases = [
'csrf' => CSRF::class,
'toolbar' => DebugToolbar::class,
'honeypot' => Honeypot::class,
'invalidchars' => InvalidChars::class,
'secureheaders' => SecureHeaders::class,
'auth' => FilterJWT::class,
];
public $filters = [
'auth' => [
'before' => [
'api/users/*',
'api/users'
]
]
];
***
ModelUsers.php
namespace App\Models;
use App\Libraries\MongoDb;
class ModelUsers
{
private $database = 'ci4_api';
private $collection = 'user';
private $conn;
function __construct()
{
$mongodb = new MongoDb();
$this->conn = $mongodb->getConn();
}
function get_user_list() {
try {
$filter = [];
$query = new \MongoDB\Driver\Query($filter);
$result = $this->conn->executeQuery($this->database. '.' . $this->collection, $query);
return $result->toArray();
} catch (\MongoDB\Driver\Exception\RuntimeException $ex) {
show_error('Error while fetching users: ' . $ex->getMessage(), 500);
}
}
***
Auth.php
namespace App\Controllers\Api;
use App\Controllers\BaseController;
use App\Models\ModelUsers;
use CodeIgniter\API\ResponseTrait;
use CodeIgniter\Validation\Validation;
use Config\Services;
class Auth extends BaseController
{
use ResponseTrait;
private ModelUsers $model;
private Validation $validation;
function __construct()
{
$this->model = new ModelUsers();
$this->validation = Services::validation();
}
public function index()
{
$email = $this->request->getVar('email');
$password = $this->request->getVar('password');
$password_hash = password_hash($password, PASSWORD_DEFAULT);
$data1 = [
'email' => $email,
'password' => $password
];
if (!$this->validation->run($data1, 'login')) {
$errors = $this->validation->getErrors();
$response = [
'status' => 201,
'error' => null,
'messages' => [
'errors' => [
$errors
]
],
];
return $this->respond($response);
}
$data1 = $this->model->get_email($email);
//return $this->respond($data1, 200);
if (!$data1) {
$response = [
'status' => 201,
'error' => null,
'messages' => [
'error' => 'Data user atau password tidak ada1'
],
];
return $this->respond($response, 200);
}
$password_user = $data1->password;
if (password_verify($password_hash, $password_user) != 0){
$response = [
'status' => 201,
'error' => null,
'messages' => [
'error' => 'Data user atau password tidak ada2'
],
];
return $this->respond($response, 200);
}
helper('jwt');
$response = [
'message' => 'Auth berhasil dilakukan',
'data' => $data1,
'access_token' => createJWT($email)
];
return $this->respond($response, 200);
}
***
users.php
namespace App\Controllers\Api;
use App\Controllers\BaseController;
use App\Models\ModelUsers;
use CodeIgniter\API\ResponseTrait;
use CodeIgniter\HTTP\Response;
use CodeIgniter\Validation\Validation;
use Config\Services;
class Users extends BaseController
{
use ResponseTrait;
private ModelUsers $model;
private Validation $validation;
function __construct()
{
$this->model = new ModelUsers();
$this->validation = Services::validation();
}
public function index(): Response
{
$data = $this->model->get_user_list();
$count = count($data);
if ($count <= 0) {
$data = [
'status' => 201,
'error' => null,
'message' => [
'success' => 'Tidak ada data daftar pegawai'
],
];
}
return $this->respond($data, 200);
}
***
REST Client
.env file
***
app.baseURL = 'http://ci4-test.localhost'
***
Routes.php
***
$routes->get('/rest', [\App\Controllers\Rest\RestClient::class, 'index']);
***
RestClient.php
namespace App\Controllers\Rest;
use App\Controllers\BaseController;
use Config\Services;
class RestClient extends BaseController
{
public function index()
{
$client = Services::curlrequest();
$token = "someToken";
$url = "http://ci4-api.localhost/api/users/";
$headers = [
'Authorization' => 'Bearer ' . $token,
];
$response = $client->request('GET', $url, ['headers' => $headers, 'http_errors' => false]);
return $response->getBody();
}
}
Postman
api auth
api all user list
I have already tried some simple solution, like reload all laragon service like apache server and mongodb, restart the windows and tried find online, but it only suggest that the url is incorectly used like in this one []https://stackoverflow.com/questions/56700991/codeigniter-301-moved-permanently[3]
Is there anyone have same issue or solution, thanks in advance.
After trying some few more time, i found the problem. It still around about url similiar like in case of Codeigniter 301 Moved Permanently, but my problem i added "/" on my url.
eg
RestClient.php
//Read all users
$url = "http://ci4-api.localhost/api/users/";
Maybe i added it after copy paste process
so the correct url is
RestClient.php
//Read all users
$url = "http://ci4-api.localhost/api/users";
hopefully help some people facing same problem

Recurring payment with Express Checkout

TL;DR: I'm trying to implement subscription to my store, but the "paypalobjects.com/api/checkout.js" redirects to "sandbox.paypal.com/webapps/hermes/error". Regular payments work as intended. I'm using Express Checkout advanced server integrations.
My Paypal.Button:
paypal.Button.render({
env: 'sandbox', // Optional: specify 'sandbox' environment
payment: function(resolve, reject) {
const token = localStorage.getItem('_token').split(' ')[1];
if(!subscribe){
var CREATE_PAYMENT_URL = `/store/${store}/paypal/create-payment/${orderId}?token=${token}`;
}else{
var CREATE_PAYMENT_URL = `/store/${store}/subscribe/paypal/create-payment/${orderId}?token=${token}`;
}
paypal.request.post(CREATE_PAYMENT_URL)
.then(function(data) { resolve(data.id); })
.catch(function(err) { console.log(err); });
},
onAuthorize: function(data) {
const token = localStorage.getItem('_token').split(' ')[1];
if(!subscribe){
var EXECUTE_PAYMENT_URL = `/store/${store}/paypal/execute-payment?token=${token}`;
}else{
var EXECUTE_PAYMENT_URL = `/store/${store}/subscribe/paypal/execute-payment?token=${token}`;
}
paypal.request.post(EXECUTE_PAYMENT_URL,
{ paymentID: data.paymentID, payerID: data.payerID })
.then(function(data) {
})
.catch(function(err) { console.log("error " + err);});
},
onCancel: function(data){
cancel();
this.destroy();
},
onError: function (err) {
console.log("ERROR OCCURRED!");
console.log(err);
}
}, '#paypal-button');
Not really relevant, but my backend looks like this(with testdata):
public function createPaypalOrder(Request $request, $store, $orderId){
$order = Order::with(['user', 'whitelabel'])->where('id', $orderId)->first();
$amout = array(
'value' => (string) $order->price/100,
'currency' => 'NOK',
);
$shippingandtax = array(
'value' => '0',
'currency' => 'NOK',
);
$charge_models = array([
'type'=> 'SHIPPING',
'amount'=> $shippingandtax,
],
[
'type'=> 'TAX',
'amount'=> $shippingandtax,
]);
$payment_definitions_creation = array();
array_push($payment_definitions_creation,[
'name' => 'Regular Payment Definition',
'type' => 'REGULAR',
'frequency' => 'MONTH',
'frequency_interval'=> '2',
'amount' => $amout,
'cycles' => '12',
'charge_models' => $charge_models
]);
$format = Config::get('constants.FRONTEND_URL')[env('APP_ENV')];
$redirectBase = sprintf($format, $order->whitelabel->subdomain, 'orders/?order=' . $order->id);
$merchant_preferences_temp = array(
'value' => '0',
'currency' => 'NOK'
);
$merchant_preferences = array(
"setup_fee" => $merchant_preferences_temp,
'return_url' => "http://www.vg.no",
'cancel_url' => "http://www.yahoo.no",
'auto_bill_amount' => 'YES',
'initial_fail_amount_action' => 'CONTINUE',
'max_fail_attempts' => '0'
);
$payment_definitions = array();
array_push($payment_definitions, $payment_definitions_creation);
$name = 'Monthly subscription to ' . (string)$order->whitelabel->title;
$body = array(
'name' => $name,
'description' => 'Subscribtion.',
'type' => 'fixed',
'payment_definitions' => $payment_definitions_creation,
"merchant_preferences"=> $merchant_preferences,
);
$token = $this->getPaypalToken($order);
$client = new \GuzzleHttp\Client();
$response = $client->post('https://api.sandbox.paypal.com/v1/payments/billing-plans', [
'headers' => ['Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $token],
'json' => $body,
]);
$paypalOrderCreation = json_decode($response->getBody());
// add stuff to db
$order->setTransactionId($paypalOrderCreation->id);
return json_encode($paypalOrderCreation);
}
My backend returns a valid response from paypal with the id of the order and the state "CREATED". (And lots of other data..)
{"id":"P-0SE01606VF925501Y2UAKG3Y","state":"CREATED","name":"Monthly subscription to Paypal","description":"Subscribtion.","type":"FIXED","payment_definitions":[{"id":"PD-35U317461H38251442UAKG4A","name":"Regular Payment Definition","type":"REGULAR","frequency":"Month","amount":{"currency":"NOK","value":"500"},"cycles":"12","charge_models":[{"id":"CHM-7T021625H451740052UAKG4A","type":"SHIPPING","amount":{"currency":"NOK","value":"0"}},{"id":"CHM-313690493W320615U2UAKG4A","type":"TAX","amount":{"currency":"NOK","value":"0"}}],"frequency_interval":"2"}],"merchant_preferences":{"setup_fee":{"currency":"NOK","value":"0"},"max_fail_attempts":"0","return_url":"http:\/\/www.vg.no","cancel_url":"http:\/\/www.yahoo.no","auto_bill_amount":"YES","initial_fail_amount_action":"CONTINUE"},"create_time":"2017-01-25T09:41:45.967Z","update_time":"2017-01-25T09:41:45.967Z","links":[{"href":"https:\/\/api.sandbox.paypal.com\/v1\/payments\/billing-plans\/P-0SE01606VF925501Y2UAKG3Y","rel":"self","method":"GET"}]}
Now my problem is that when my paypal.button receives this response it processes the information and redirects me to "sandbox.paypal.com/webapps/hermes/error", which is somewhat hard to debug.
Thanks :)
The problem was an internal issue at Paypal. Works now.

How to call the API with token

I'm using https://github.com/Zizaco/entrust for my ReST API integration in Laravel 5.2
I can retrieve the token. Then, I try to pass it to some route which needs the token. But I get this error with status code 401:
Failed to authenticate because of bad credentials or an invalid authorization header.
This is the code I use:
$base_url = 'http://mysite/api/';
$fields = array(
'email' => urlencode('myEmail'),
'password' => urlencode('myPass'),
);
$fields_string = '';
foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; }
rtrim($fields_string, '&');
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL, $base_url . 'login');
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
curl_setopt($ch,CURLOPT_POST, count($fields));
curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string);
$result = curl_exec($ch);
$result = json_decode($result, true);
$token = false;
if ($result['result'] == 'success') {
$token = $result['token'];
}
if ($token) { //call the api by passing the token:
// var_dump($token);
curl_setopt($ch,CURLOPT_URL, $base_url . 'admin/sells');
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Authorization: Bearer ' . $token
]);
curl_setopt($ch,CURLOPT_POST, null);
curl_setopt($ch,CURLOPT_POSTFIELDS, null);
curl_setopt($ch, CURLOPT_HTTPGET, 1);
// var_dump(curl_getinfo($ch));
$result = curl_exec($ch);
$result = json_decode($result, true);
var_dump($result);
}
And this is my route:
$api = app('Dingo\Api\Routing\Router');
$app_controller = 'App\\Http\\Controllers\\';
$auth_controller = 'App\\Http\\Controllers\\Auth\\';
$api->version('v1', function ($api) use ($app_controller,$auth_controller) {
$api->post('login', ['as' => 'api_login', 'uses' => $auth_controller . 'AuthController#authenticate']);
});
$api->version('v1', ['middleware' => 'api.auth'], function ($api) use ($app_controller,$auth_controller) {
$api->get('admin/sells', ['as' => 'api_admin_sells', 'uses' => $app_controller . 'SellsController#apiIndex']);
});
Am I doing anything wrong?

plentymarkets add product via soap

im trying to insert product to plentymarkets via soap, but i get this error :
object(stdClass)#14 (2) { ["Success"]=> bool(false) ["ResponseMessages"]=> object(stdClass)#18 (0) { } }
here is my code
$client = new SoapClient($soapUrl);
$aHeader = array(
'UserID' => '12',
'Token' => 'ece85f3bc7a2783b164bf18ed882b6d6' //etcimport
);
$auth_vals = new SoapVar($aHeader, SOAP_ENC_OBJECT);
$ns = "Authentification";
$oSoapHeader = new SoapHeader($ns,'verifyingToken', $auth_vals, false);
$client = new SoapClient($soapUrl);
$client->__setSoapHeaders($oSoapHeader);
$result = $client->AddItemsBase(array(
'ItemNo' => 123,
'ExternalItemID' => 345,
'EAN1' => 4011200296908
));
var_dump($result);

php facebook application user wall post

i am create a Facebook application. I am try to post users wall.But i do not get any response
php code
$user = $facebook->getUser();
if($user == 0) {
$login_url = $facebook->getLoginUrl($params = array('scope' => "publish_stream"));
echo ("<script> top.location.href='".$login_url."'</script>");
} else { $token=$facebook->getAccessToken();
try {
params = array(
'message' => "Hurray! This works :)",
'name' => "This is my title",
'caption' => "My Caption",
'description' => "Some Description...",
'link' => "http://stackoverflow.com",
'picture' => "http://i.imgur.com/VUBz8.png",
);
$post = $facebook->api("/me/feed","POST",$params);
}
catch (FacebookApiException $e) {
$result = $e->getResult();
}
}
i can't post the content into user's wall .Please help me any one to debug the code
Try this, I posted successfully on my FB using the following:
$user = $facebook->getUser();
if($user == 0) {
//$login_url = $facebook->getLoginUrl($params = array('scope' => "publish_stream"));
//echo ("<script> top.location.href='".$login_url."'</script>");
} else { $token=$facebook->getAccessToken();
try {
$post = $facebook->api("/me/feed","POST",array(
'message' => "Hurray! This works :)",
'name' => "This is my title",
'caption' => "My Caption",
'description' => "Some Description...",
'link' => "http://stackoverflow.com",
'picture' => "http://i.imgur.com/VUBz8.png"
));
}
catch (FacebookApiException $e) {
$result = $e->getResult();
}
}
If you haven't already, remember to include the facebook.php api and your code class initialization
$facebook = new Facebook(array(
'appId' => APP_ID,
'secret' => APP_SECRET,
'cookie' => true
));
try post with php curl. if works your facebook class using rest api. Rest api deleted recently.
$user = $facebook->getUser();
if($user == 0) {
$login_url = $facebook->getLoginUrl($params = array('scope' => "publish_stream"));
echo ("<script> top.location.href='".$login_url."'</script>");
} else { $token=$facebook->getAccessToken();
try {
$params = array(
'access_token' => $token,
'message' => "Hurray! This works :)",
'name' => "This is my title",
'caption' => "My Caption",
'description' => "Some Description...",
'link' => "http://stackoverflow.com",
'picture' => "http://i.imgur.com/VUBz8.png",
);
$url = 'https://graph.facebook.com/'.$user.'/feed';
$ch = curl_init();
curl_setopt_array($ch, array(
CURLOPT_URL => $url,
CURLOPT_POSTFIELDS => $params,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_VERBOSE => true
));
$result = curl_exec($ch);
print_r($result);
curl_close($ch);
}
catch (FacebookApiException $e) {
$result = $e->getResult();
}
}
in your code params is not a $params.