I am trying to test synchronized Payout for my PHP application but response from server is always the same and it points me to INTERNAL_ERROR. I am using paypal/rest-api-sdk-php I believe of version v1.0.0 (composer.json says nothing) - should be the newest in my JSON I have version set to dev-master.
try {
/** #var PayoutBatch $payoutBatch */
$payoutBatch = $payout->createSynchronous($apiContext);
} catch (\Exception $ex) {
echo $ex->getMessage();
}
try {
/** #var PayoutBatch $response */
$response = Payout::get($payoutBatch->getBatchHeader()->getPayoutBatchId(), $apiContext);
$status = $response->getBatchHeader()->getBatchStatus();
if ($status === "SUCCESS") {
//do some logic
}
} catch (\Exception $ex) {
echo $ex->getMessage();
}
Have any of you experienced this issue?
Also a sub-question, don't PayPal PHP SDK has defined some constants where I can find message statuses?
Can you please make sure you have Payouts Enabled in your sandbox environment. This allows you to get the proper scope that is required to make payout calls.
However, if you have already done that and still failing, can you please try the samples out that are shipped along with SDK.
All you need to do is run one command, and you will have the samples running, as mentioned here
php -f vendor/paypal/rest-api-sdk-php/sample/index.php
This will start a sample server, and allow you to play around it.
It has a payout samples, that you could just click and run. If those works, we can now make one tiny change to get samples to use your clientId and secret. Find samples/bootstrap.php in sdk code, and change those clientId and secret, and run the samples again. If you get a failure, let me know and I could look further into that issue.
Related
I'm trying to integrate Stripe to accept payments on a website. I've come to the point where pretty much everything works from the client's perspective - test payments go through, everything is integrated and I can do stuff on payment intent succeeded (on the client). However, I can only detect that on the front-end.
Now, I want to show customer some sensitive data when payment is completed. I can't do it on front end - because then client does not even have to pay for that. I need to use webhooks.
So, I've created a sample webhook like this:
require_once 'stripe-php-7.97.0/init.php';
$endpoint_secret = 'endpoint_secret_code';
$payload = #file_get_contents('php://input');
$sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
$event = null;
try {
$event = \Stripe\Webhook::constructEvent(
$payload, $sig_header, $endpoint_secret
);
} catch(\UnexpectedValueException $e) {
// Invalid payload
http_response_code(400);
exit();
} catch(\Stripe\Exception\SignatureVerificationException $e) {
// Invalid signature
http_response_code(400);
exit();
}
ob_flush();
ob_start();
var_dump($event);
file_put_contents("dump.txt", ob_get_flush());
// Handle the event
switch ($event->type) {
case 'payment_intent.succeeded':
$paymentIntent = $event->data->object;
var_dump($event->type);
// ... handle other event types
default:
echo 'Received unknown event type ' . $event->type;
}
http_response_code(200);
I've also created a webhook in the Stripe dashboard, like this:
However, based on the payment amount, I need to do something on this web hook - create a session, store data into the database and so on and so on. I've made it possible with Stripe CLI to test this webhook. However, how to test it using the test payments on the actual page? I have no idea how to do that - it does not work at the moment and without it, I can't be sure that it will work properly once everything goes live until I properly test it like this.
Does anyone have any tips, tutorials, or any kind of help to solve this? I'm pretty much stuck on it so any help would be greatly appreciated.
Summary
I am trying to set up heartbeats with my Consumer so it can detect a dropped connection - if the broker is rebooted for example so it may reconnect to the failover.
I tested the code with the Consumer/Dispatcher running locally and the queue running in AWS and everything worked fine. However, when moving the code to AWS the Consumer sets up heartbeats with the server/broker, but the heartbeat is either never sent by the server/broker or never received by the client/Consumer. As a result the HeartbeatException is thrown as soon as the requested server heartbeat interval has passed.
I based my code on the examples in the stomp-php-examples github
Repo: https://github.com/stomp-php/stomp-php-examples
Heartbeat example: https://github.com/stomp-php/stomp-php-examples/blob/support/version-4/src/heartbeats_server.php
My next best guess to as why this isn't working is something related to the queue config as I'm using the default config provided by AWS (I think). I have googled and searched all over about config settings for heartbeats but haven't got very far as this is a new topic. Any help would be appreciated!
Setup
Amazon MQ (ActiveMQ 5.15.14)
stomp-php 5.0 (latest version as of today)
I'm happy to provide any more details of my setup.
Code
Consumer (stripped down version)
abstract class AmqConsumerAbstract
{
/** #var StatefulStomp */
protected $consumer;
const HEARTBEAT = 5000;
public function listen(): void
{
$observer = new ServerAliveObserver();
$this->client->getConnection()->getObservers()->addObserver($observer);
$this->client->setHeartbeat(0, self::HEARTBEAT); // heartbeats setup here
// Note: the heartbeat above is longer than the read-timeout below
$this->client->getConnection()->setReadTimeout(2, 0);
$this->client->connect();
$this->consumer = new StatefulStomp($this->client);
$this->consumer->subscribe(
$this->getQueueName(),
null,
'client'
);
if (!$observer->isEnabled()) {
// we never get here so I assume everything is working OK
echo 'The Server is not supporting heartbeats.');
exit(1);
} else {
echo sprintf('The Server should send us signals every %d ms.', $observer->getInterval() * 1000);
}
try {
while (true) {
$frame = $this->consumer->read(); // I assumed this line would read the heartbeat?
// there is then some logic that deals with payload and does
// $this->consumer->begin();
// $this->consumer->ack($frame);
// $this->consumer->commit();
if ($observer->isDelayed()) {
$this->echoLog('ServerAliveObserver: Server has been delayed.');
}
}
} catch (HeartbeatException $e) {
echo 'AMQ (STOMP) Error, the server failed to send us heartbeats within the defined interval: ' . $e->getMessage()
$this->reconnect();
} catch (ConnectionException $e) {
echo $e->getMessage();
$this->reconnect();
} catch (Exception $e) {
echo 'AMQ (STOMP) Queue error: ' . $e->getMessage();
exit(1);
}
}
}
I have reproduced on a single-instance broker with version 5.15.14.
I have tried switching on STOMP debugging for the broker, but it appears the uri attribute under <transportConnector> is not allowed. The config will save but pulls out the uri attribute.
<transportConnector> Elements and Their Attributes Permitted in Amazon MQ Configurations
Working with Spring XML configuration files (xsd files)
I am using the PayPal PHP SDK found here: https://github.com/paypal/Checkout-PHP-SDK
And I am somewhat puzzled in terms of how to complete the process.
On the outset this seems quite simple:
Setup your credentials
Create the Order
Check the result, and re-direct to approval link
User makes a payment and is sent to the SUCCESS link that you would have set.
i.e. http://example.com/pay/complete/paypal?token=8UK32254ES097084V&PayerID=SEQNPLB2JR9LY
And this is where things get a bit shakey.
Conveniently, a token and a PayerID is returned.
And according to the documentation, you now need to "Capturing the Order" and the following code is provided:
use PayPalCheckoutSdk\Orders\OrdersCaptureRequest;
// Here, OrdersCaptureRequest() creates a POST request to /v2/checkout/orders
// $response->result->id gives the orderId of the order created above
$request = new OrdersCaptureRequest("APPROVED-ORDER-ID");
$request->prefer('return=representation');
try {
// Call API with your client and get a response for your call
$response = $client->execute($request);
// If call returns body in response, you can get the deserialized version from the result attribute of the response
print_r($response);
}catch (HttpException $ex) {
echo $ex->statusCode;
print_r($ex->getMessage());
}
What is confusing is that the OrdersCaptureRequest requires an "APPROVED-ORDER-ID"
But all that has been returned is a "token" and a "PayerID".
So my question is, what is this APPROVED-ORDER-ID, and where do I get it?
Thank you!
what is this APPROVED-ORDER-ID, and where do I get it
At that moment, sourced from token= . It should correspond to an Order Id you received in the response to your step 2 ("Create the Order")
For step 3, it is better to use no redirects whatsoever. Instead, implement this front-end UI, which offers a far superior in-context experience that keeps your site loaded in the background: https://developer.paypal.com/demo/checkout/#/pattern/server
There is no reason for a modern website to be redirecting unnecessarily
I'd like to validate my in-app purchases.
I'm using this receipt validator which by looking at the source code is calling https://www.googleapis.com/androidpublisher/v3/applications/<appId>/purchases/products/<productId>/tokens/<token>, here's my code basically copied from the readme
$googleClient = new Google_Client();
$googleClient->setScopes([Google_Service_AndroidPublisher::ANDROIDPUBLISHER]);
$googleClient->setApplicationName('my project name');
$googleClient->setAuthConfig('files/googleauthconfig.json');
$validator = new Validator(new Google_Service_AndroidPublisher($googleClient));
try {
$response = $validator->setPackageName('my.app.id')
->setProductId('my.product.id')
->setPurchaseToken('token i got from my app when purchasing')
->validatePurchase();
} catch (Exception $e) {
var_dump($e->getMessage());
exit;
}
var_dump($response);
exit;
I've hardcoded a lot of stuff because I'm just trying to get it to work at this point. googleauthconfig.json is the JSON file dev console gave me for my service account which contains a bunch of IDs for the account as well as a private key. I went in my play console and made my service account an administrator and then waited about 24 hours as suggested in other questions for the same problem. I've configured my products, which are consumable and active and I successfully purchase them through my signed release app with the test credit card.
The error I keep getting is
The current user has insufficient permissions to perform the requested operation.
The current user has insufficient permissions to perform the requested operation.
Means exactly that the user you are authenticating with does not have the permissions to do what you are trying to do. You are using a service account. A service account is not you. Service accounts are preauthorized.
Go over to google pay probably the admin section take the Service account email address and grant it permissions to access the data. You probably do this like you would any other user.
As mentioned in the comment it can take time normally under an hour for a service account to kick in. After you grant it access.
possible code error.
I think you should try the pure code rather that using your validator to ensure that its not thats the problem.
$client = new Google_Client();
$client->setScopes([Google_Service_AndroidPublisher::ANDROIDPUBLISHER]);
$client->setApplicationName('my project name');
$client->setAuthConfig('files/googleauthconfig.json');
$service = new Google_Service_AndroidPublisher($client);
try {
$response = $service->purchases_products->get(package_name, product_id, purchase_token);
} catch (Exception $e) {
var_dump($e->getMessage());
exit;
}
var_dump($response);
exit;
Well, oddly enough, as suggested in other answers as well, I just needed to wait in my case about 30 hours before the permission changes took effect.
I'm using the following class to process the IPN data:
https://github.com/WadeShuler/PHP-PayPal-IPN (and it has been working fine for ages)
The last successfully processed payment was on September 28, the next IPN payment that came though on October 5 returned a response of INVALID, as has every payment since.
If I test in the IPN simulator, the message processes successfully.
Sandbox or Live, the response is INVALID. I would have thought the Sandbox would have a facility where I can review why a message was rejected as INVALID, but that doesn't seem to be the case? Would certainly have been the easy way to see the rejection reason, am I missing something?
The link that brings you to Paypal from my site is:
https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_ext-enter&redirect_cmd=_xclick&business=sales-facilitator#fydentry.com&item_name=QLD++%28Pacific+pines+secondary+Sat+25th+July+%29&item_number=FYD15000003&custom=0001000003¤cy_code=AUD&amount=0.01&no_shipping=1&image_url=http://www.fydentry.com/img/follow-your-dreams-100.jpg&return=http://www.fydentry.com/entry-test.php&cancel_return=http://www.fydentry.com/entry-test.php¬ify_url=http://www.fydentry.com/payback-test.php
The data posted back from Paypal is:
transaction_subject=0001000003&txn_type=web_accept&payment_date=07%3A05%3A07+Oct+15%2C+2015+PDT&last_name=buyer&residence_country=AU&pending_reason=multi_currency&item_name=QLD++%28Pacific+pines+secondary+Sat+25th+July+%29&payment_gross=&mc_currency=AUD&business=sales-facilitator%40fydentry.com&payment_type=instant&protection_eligibility=Ineligible&verify_sign=Acssfl2b2v1gxOK33TD2StcDhf-OAZxwix74kxFlSgpWMku6myuy.WFL&payer_status=verified&test_ipn=1&tax=0.00&payer_email=sales-buyer%40fydentry.com&txn_id=9BV63134E20871700&quantity=1&receiver_email=sales-facilitator%40fydentry.com&first_name=test&payer_id=FY3RYW98GNMXG&receiver_id=95FC3QCK53MHC&item_number=FYD15000003&handling_amount=0.00&payment_status=Pending&shipping=0.00&mc_gross=0.01&custom=0001000003&charset=windows-1252¬ify_version=3.8&ipn_track_id=cfbd422d97d69
The response I send back to Paypal is:
cmd=_notify-validate&transaction_subject=0001000003&txn_type=web_accept&payment_date=07%3A05%3A07+Oct+15%2C+2015+PDT&last_name=buyer&residence_country=AU&pending_reason=multi_currency&item_name=QLD++%28Pacific+pines+secondary+Sat+25th+July+%29&payment_gross=&mc_currency=AUD&business=sales-facilitator%40fydentry.com&payment_type=instant&protection_eligibility=Ineligible&verify_sign=Acssfl2b2v1gxOK33TD2StcDhf-OAZxwix74kxFlSgpWMku6myuy.WFL&payer_status=verified&test_ipn=1&tax=0.00&payer_email=sales-buyer%40fydentry.com&txn_id=9BV63134E20871700&quantity=1&receiver_email=sales-facilitator%40fydentry.com&first_name=test&payer_id=FY3RYW98GNMXG&receiver_id=95FC3QCK53MHC&item_number=FYD15000003&handling_amount=0.00&payment_status=Pending&shipping=0.00&mc_gross=0.01&custom=0001000003&charset=windows-1252¬ify_version=3.8&ipn_track_id=cfbd422d97d69
And the code in payback-test.php is:
<?php
include_once("f_common.php");
include_once("IpnListener.php");
use wadeshuler\paypalipn\IpnListener;
$listener = new IpnListener();
$listener->use_sandbox = true;
$dbh = open_db();
$res = 'UNKNOWN';
try {
$listener->requirePostMethod();
$verified = $listener->processIpn();
} catch (Exception $e) {
error_log($e->getMessage());
exit(0);
}
if ($verified) {
$res = "VERIFIED";
} else {
$res = "INVALID";
}
$stmt_debug = $dbh->prepare('INSERT INTO fyd_paypal_ipn (post_data, response_data, status, message_time) VALUES (:post_data, :response_data, :status, UTC_TIMESTAMP())');
$stmt_debug->execute(array(':post_data' => $listener->rawPostData, ':response_data' => $listener->debug_response, ':status' => $res));
?>
Note that for the purpose of this exercise, I also modified the IpnListener class so that I can get a copy of the response for debugging purposes.
At the top of the file in the variable declarations, I made $rawPostData public, and added this variable:
public $debug_response;
And store the value right before we use it:
$this->debug_response = $req;
if ($this->use_curl) {
$res = $this->curlPost($req);
} else {
$res = $this->fsockPost($req);
}
I'm assuming something has changed on the Paypal side to stop this working in the first place, but I can't see what it is, particularly as my response appears to be correct (identical), and I can't find anything in the Sandbox side that helps me identify why it is being regarded as invalid? Unfortunately the debug code at the bottom where I track the IPN post data and response is new, so I don't have an older (working!) set of messages to compare the contents to.
hummmm, since 1 october paypal change sha-1 to sha-2
https://www.paypal-knowledge.com/infocenter/index?page=content&id=FAQ1766&expand=true&locale=en_US
I've gotten to the bottom of it, it appears to be a certificate issue, combined with a script error.
My certificate file was out of date to the one currently in Wade's code. I spotted that and updated the certificate file yesterday. I've now verified that this was the root cause of my original failure.
However, while updating that, I also updated to the latest class - which was really just removing some deprecated code that I wasn't using and is no longer supported, but I missed one small change which broke things.
There's a line in the IpnListener class which sets the certificate location:
curl_setopt($ch, CURLOPT_CAINFO, dirname(dirname(__FILE__)) . '/cert/api_cert_chain.crt');
But the directory name ends up wrong on my server, and it can't find the certificate. The previous version of the class read:
curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/cert/api_cert_chain.crt');
which worked. Now that I've changed it back, everything works again. Note that the domain I'm working from is set up in a sub-directory to my primary domain, which is possibly why the new code ends up pointing to the wrong place (one level above where I need it to be).
And I'm still confused as to why the IPN simulator worked, while the Sandbox and Live systems didn't.