Symfony2 Authorize.net SIM error - symfony

I want to use Authorize.net SIM payment method in Symfony using payum.org.
There is no official gateway for it but there is one in omnipay: omnipay-authorizenet. There is also omnipay-bridge in payum so it is possible to use omnipay gateways in payum.
So I use this setup and after submitting the authorize.net form I get the error:
[date] request.CRITICAL: Uncaught PHP Exception Omnipay\Common\Exception\InvalidRequestException: "Incorrect hash" at .../authorize/vendor/omnipay/authorizenet/src/Message/SIMCompleteAuthorizeRequest.php line 42 {"exception":"[object] (Omnipay\\Common\\Exception\\InvalidRequestException(code: 0): Incorrect hash at .../authorize/vendor/omnipay/authorizenet/src/Message/SIMCompleteAuthorizeRequest.php:42)"} []
BUT this is NOT because of the generated hashes being incorrect - it is because capture url is called second time without the POST data.
On a clean installation of Symfony2 with 3 packages:
composer.json:
"payum/payum-bundle": "0.15.*",
"omnipay/authorizenet": "~2.0",
"payum/omnipay-bridge": "*#stable"
config.yml:
payum:
security:
token_storage:
AppBundle\Entity\PaymentToken: { doctrine: orm }
storages:
AppBundle\Entity\Payment: { doctrine: orm }
gateways:
authorizeGateway:
omnipay_offsite:
type: AuthorizeNet_SIM
options:
hashSecret: 'Simon'
ApiLoginId: 'xxx'
transactionkey: 'xxx'
testMode: false
developerMode: true
Controller:
/**
* #Route("/prepare", name="prepare")
*/
public function prepareAction()
{
$gatewayName = 'authorizeGateway';
$storage = $this->get('payum')->getStorage('AppBundle\Entity\Payment');
$payment = $storage->create();
$payment->setNumber(uniqid());
$payment->setCurrencyCode('USD');
$payment->setTotalAmount(1);
$payment->setDescription('A description');
$payment->setClientId('anId');
$payment->setClientEmail('foo#example.com');
$storage->update($payment);
$captureToken = $this->get('payum.security.token_factory')->createCaptureToken(
$gatewayName,
$payment,
'done' // the route to redirect after capture
);
return $this->redirect($captureToken->getTargetUrl());
}
/**
* #Route("/done", name="done")
*/
public function doneAction(Request $request)
{
...
}
Going to /prepare shows a redirecting to authorize.net page for a second and I'm redirected to external test.authorize.net/gateway/transact.dll (on https) page where I specify card number (test card number) and expiration date in the future.
Submitting this form gives:
An error occurred while trying to report this transaction to the merchant. An e-mail has been sent to the merchant informing them of the error. The following is the result of the attempt to charge your credit card.
This transaction has been approved.
It is advisable for you to contact the merchant to verify that you will receive the product or service.
I'm getting the email about Merchant Email Receipt and the one about the error:
Authorize.Net Developer Center Merchant,
Your script timed out while we were trying to post transaction results to it.
Transaction ID: XXX
Transaction Result: This transaction has been approved.
The transaction is processed correctly, the capture script is called, hashes match and then the capture is called again without post data - then hashes don't match and authorize displays error.
Requests that are made from symfony profiler:
Token IP Method URL Time Status
fe39ec 198.241.162.104 GET .../payment/capture/vVgoUCPtgCOglv6rLwhIbUp64RZ_oIql1_KDpWjdrdk Tue, 17 Nov 2015 09:47:36 +0100 500
bba47c 198.241.162.104 GET .../payment/capture/vVgoUCPtgCOglv6rLwhIbUp64RZ_oIql1_KDpWjdrdk Tue, 17 Nov 2015 09:47:36 +0100 200
c95b83 198.241.162.104 POST .../payment/capture/vVgoUCPtgCOglv6rLwhIbUp64RZ_oIql1_KDpWjdrdk Tue, 17 Nov 2015 09:47:36 +0100 302
a87347 myip GET .../payment/capture/vVgoUCPtgCOglv6rLwhIbUp64RZ_oIql1_KDpWjdrdk Tue, 17 Nov 2015 09:47:30 +0100 200
c95d57 myip GET .../prepare Tue, 17 Nov 2015 09:47:29 +0100 302
From what i see when we call /prepare we get redirected to capture right away this goes to authorize's form. Then after a few seconds (when credit card data is filled in and submitted) authorize (different ip) makes post request to capture. This is 302 redirect (and probably should be a SIM response with javascript code to go back to our page?). Capture is called secod time with GET and calculated hashes don't match - this is 500 response - authorize stays on their url and shows the error message. Done script is never called.
What can be the issue? It's difficult to debug this further because there is payum, omnipay-bridge, omnipay, authorize combined.
Im testing this on the environment accessible from the internet with account on http://developer.authorize.net/ with test mode off.
UPDATE:
If I add notify token to the controller, like this:
/**
* #Route("/prepare", name="prepare")
*/
public function prepareAction()
{
$gatewayName = 'authorizeGateway';
$storage = $this->get('payum')->getStorage('AppBundle\Entity\Payment');
$payment = $storage->create();
$payment->setNumber(uniqid());
$payment->setCurrencyCode('USD');
$payment->setTotalAmount(1); // 1.23 EUR
$payment->setDescription('A description');
$payment->setClientId('anId');
$payment->setClientEmail('foo#example.com');
$storage->update($payment);
$captureToken = $this->get('payum.security.token_factory')->createCaptureToken(
$gatewayName,
$payment,
'done' // the route to redirect after capture
);
$tokenFactory = $this->get('payum.security.token_factory');
$notifyToken = $tokenFactory->createNotifyToken($gatewayName, $payment);
$payment->setDetails(['notifyUrl' => $notifyToken->getTargetUrl()]);
$storage->update($payment);
return $this->redirect($captureToken->getTargetUrl());
}
I get error "Request Notify{model: ArrayObject} is not supported.":
[2015-11-17 17:46:50] request.INFO: Matched route "payum_notify_do". {"route_parameters":{"_controller":"Payum\\Bundle\\PayumBundle\\Controller\\NotifyController::doAction","payum_token":"Lv5ovrC-8vikIB9ItDVLcNfuRzjjaD_pPiE3-6VIV8Y","_route":"payum_notify_do"},"request_uri":".../payment/notify/Lv5ovrC-8vikIB9ItDVLcNfuRzjjaD_pPiE3-6VIV8Y"} []
[2015-11-17 17:46:50] security.INFO: Populated the TokenStorage with an anonymous Token. [] []
[2015-11-17 17:46:50] request.CRITICAL: Uncaught PHP Exception Payum\Core\Exception\RequestNotSupportedException: "Request Notify{model: ArrayObject} is not supported." at .../authorize/vendor/payum/core/Payum/Core/Exception/RequestNotSupportedException.php line 29 {"exception":"[object] (Payum\\Core\\Exception\\RequestNotSupportedException(code: 0): Request Notify{model: ArrayObject} is not supported. at .../authorize/vendor/payum/core/Payum/Core/Exception/RequestNotSupportedException.php:29)"} []

Omnipay bridge 0.15.x does not set a notifyUrl, and the omnipay gateway uses return url as notify one. When notification comes (before you are redirected) the capture token is invalidated and no longer available.
There are two solutions:
Upgrade to 1.0 where notifyUrl is generated. Btw you can use omnipay gateway factory instead of omnipay_offsite.
or you have to generate notify url yourself, and set it to notifyUrl
$tokenFactory = $this->get('payum.security.token_factory');
$notifyToken = $tokenFactory->createNotifyToken($gatewayName, $payment);
$payment->setDetails(['notifyUrl' => $notifyToken->getTargetUrl()]);
$storage->update($payment);

Related

ClassyTaxiServer with Server error: Cannot read property 'get' of undefined

I'm implementing the ClassyTaxiAppKotlin + ClassyTaxiServer project but without success.
I followed all the steps in the tutorial.
Apparently the ClassyTaxiAppKotlin android app is functional, processes the subscription purchase order and sends the information to the CLOUD FUNCTIONS, but does not receive the purchase registration/confirmation.
In my understanding, upon receiving the payment confirmation, PLAY STORE must send this confirmation to the ClassyTaxiServer server so that the server returns the registration/confirmation of the purchase and grants access to the ClassyTaxiAppKotlin application.
So when confirming payment in ClassyTaxiAppKotlin app, I notice that I get some logs in firebase functions from (instanceId_register_v2, realtime_notification_listener AND subscription_register_v2), but the registration/confirmation of payment doesn't complete due to a server error (500) and doesn't release the access in the ClassyTaxiAppKotlin app.
Note: service-account.json is already configured
Does anyone have any idea why this error is occurring and indicate how I can solve it?
Code where the error points:
private async querySubscriptionPurchaseWithTriggerV2(packageName: string, product: string, purchaseToken: string, triggerNotificationType?: NotificationType): Promise<SubscriptionPurchaseV2> {
// STEP 1. Query Play Developer API to verify the purchase token
const apiResponseV2 = await new Promise((resolve, reject) => {
this.playDeveloperApiClient.purchases.subscriptionsv2.get({ // <<=== Server error: Cannot read property 'get' of undefined
packageName: packageName,
token: purchaseToken
}, (err, result) => {
if (err) {
reject(this.convertPlayAPIErrorToLibraryError(err));
} else {
resolve(result.data);
}
})
});
LOGS Firebase Functions
6:55:28.798 PM instanceId_register_v2 Function execution started
6:55:31.025 PM instanceId_register_v2 Instance id is ddR1Hi...NOO2Z
6:55:31.122 PM instanceId_register_v2 Instance Id specified and verified
6:55:31.122 PM instanceId_register_v2 Instance verification passed
6:55:31.774 PM instanceId_register_v2 Function execution took 2976 ms, finished with status code: 200
6:55:53.623 PM realtime_notification_listener ========> purchase: null PACKAGE_NAME: com.example.subscriptions
6:55:53.624 PM realtime_notification_listener Function execution took 5 ms, finished with status: 'ok'
6:55:57.537 PM subscription_register_v2 Function execution started
6:55:59.817 PM subscription_register_v2 Server error: Cannot read property 'get' of undefined
6:55:59.825 PM subscription_register_v2 Function execution took 2289 ms, finished with status code: 500
LOGS Android Studio
D/OkHttp: --> PUT https://us-central1-postosgnv.cloudfunctions.net/subscription_register_v2 http/1.1 (437-byte body)
D/OkHttp: <-- 500 https://us-central1-postosgnv.cloudfunctions.net/subscription_register_v2 (2661ms, 86-byte body)
E/RemoteServerFunction: Failed to call API (Error code: 500) - {"status":500,"error":"not-found","message":"Cannot read property 'get' of undefined"}
As per the answer found HERE, the issue was solved by updating the GoogleApis package in the package.json from "googleapis": "^67.0.0" -> "googleapis": "105.0.0"

Not enough permissions to access: POST /dmpSegments

I tried - POST https://api.linkedin.com/v2/dmpSegments
but got error -
{"serviceErrorCode":100,"message":"Not enough permissions to access:
POST /dmpSegments","status":403}
My app does have permission of rw_ads. I can successfully call some ads api endpoints, e.g.
- POST https://api.linkedin.com/v2/adSegmentsV2
- POST https://api.linkedin.com/v2/adCampaignGroupsV2
- POST https://api.linkedin.com/v2/adCampaignsV2
- POST https://api.linkedin.com/v2/adCreativesV2
public string CreateDmpSegment(string token, DmpSegmentsCreateRequest dmpSegmentsCreateRequest, ILogMessages messages)
{
NameValueCollection data = System.Web.HttpUtility.ParseQueryString("");
string url = $#"{LinkedInApiUrl}dmpSegments";
Tuple<NameValueCollection, dynamic> results = RestHelper.JsonApiPOST(url, token, dmpSegmentsCreateRequest);
if (string.Equals(results.Item1["valid"], true.ToString(), StringComparison.InvariantCultureIgnoreCase))
{
return results.Item2["X-LinkedIn-Id"];
}
UpdateErrors(LinkedInErrors.CreateDmpSegmentError, results, messages);
return null;
}
expected return results.Item2["X-LinkedIn-Id"];
but got error -
{"serviceErrorCode":100,"message":"Not enough permissions to access: POST /dmpSegments","status":403}
From what I read here you must have the access token generated with rw_dmp_segments scope. The rw_ads scope is not enough.
To be able to request access tokens with rw_dmp_segments scope, you have to obtain a further permission from LinkedIn as written here:
"..you must initiate a Technical Sign Off request by contacting your LinkedIn POC on the Business Development team."
Hope this helps.

.NET Core Patch JsonPatchDocument works locally but not on server

I have a very simple ASP.NET Core app which works perfectlly on my local machine.
When I publish my project everything works (GET, POST, DELETE) but not partially update (PATCH). I use JsonPatchDocument<T>. This is the code:
[HttpPatch("{id}")]
public IActionResult PartiallyUpdateNote(int id, [FromBody] JsonPatchDocument<NoteForUpdateDto> patches)
{
_log.LogInformation("Partial update of #{0}. Patches: {1}", id, patches);
if (patches == null) // <------ THIS IS ALWAYS TRUE
{
_log.LogError("Invalid patch format");
return BadRequest("Invalid patch format?");
}
This is json I try to send from Postman:
PATCH http://tomasz.look24.net/api/notes/1
headers:
Content-Type=application/json
Raw: [{ "op": "replace", "path":
"/title", "value": "ne555e" }]
This is log from server (single patch hit):
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [INF] Connection id ""0HL2VB6EGP0A9"" bad request data: ""Unexpected end of request content"" (86f1a409)
Microsoft.AspNetCore.Server.Kestrel.BadHttpRequestException: Unexpected end of request content
at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.MessageBody.ForContentLength.<ReadAsyncAwaited>d__4.MoveNext()
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [INF] Executing action method "notes.api.Controllers.NotesController.PartiallyUpdateNote (notes.api)" with arguments (["1", ""]) - ModelState is Invalid (ba7f4ac2)
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [INF] Partial update of #1. Patches: null (30b5833a)
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [ERR] Invalid patch format (b8690943)
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [INF] Executing ObjectResult, writing value "Microsoft.AspNetCore.Mvc.ControllerContext". (4e968210)
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [INF] Executed action "notes.api.Controllers.NotesController.PartiallyUpdateNote (notes.api)" in 119502.6086ms (afa2e885)
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [INF] Request finished in 119503.2131ms 400 text/plain; charset=utf-8 (15c52c40)
2017-02-27T22:46:46.1647691+01:00 [INF] Connection id ""0HL2VB6EGP0A9"" bad request data: ""Unexpected end of request content"" (86f1a409)
Microsoft.AspNetCore.Server.Kestrel.BadHttpRequestException: Unexpected end of request content
at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.MessageBody.ForContentLength.ReadAsyncImplementation(ArraySegment`1 buffer, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.MessageBody.Consume(CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame`1.<RequestProcessingAsync>d__2.MoveNext()
2017-02-27T22:46:46.1647691+01:00 [INF] Connection id ""0HL2VB6EGP0A9"" communication error (46f6d175)
Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.UvException: Error -4077 ECONNRESET connection reset by peer
I got this log after 2 minutes with message:
Server Error
502 - Web server received an invalid response while acting as a
gateway or proxy server. There is a problem with the page you are
looking for, and it cannot be displayed. When the Web server (while
acting as a gateway or proxy) contacted the upstream content server,
it received an invalid response from the content server.
What can be wrong? Is there something missing in server configuration?
When I try to get data as a string like that:
public IActionResult PartiallyUpdateNote(int id, [FromBody] string patches)
Everything is fine on the server side. There must be something with PartiallyUpdateNote (I believe) which always return null.
I found the solution.
It works when I change PATCH to POST
[HttpPost("{id}")]
I wonder why?

Symfony2, RabbitMQ: I'm lost

I've installed the RabbitMQ Bundle already. Now here is what I want to do:
Controller: Creates Redis-List, pushes message to client, afterwards send a message into queue, so heavier background task can be executed asynchronously.
But I'm lost.
$msg = array('userid' => $someid);
$this->get('old_sound_rabbit_mq.task_example_producer')->publish(serialize($msg));
This will send some data to a produce? And the according consumer will execute the heavy background task (DB queries etc, based on the "userid" from the producer)? Do I need a callback? What's the queue?! The queue forwards the messages from the producer to the consumer one by one? So can I have multiple consumers to handle more messages at the same time?!
Kinda old post but in case someone else comes looking for help:
It seems that you are using the old_sound's rabbitmq bundle. It has a somewhat helpful tutorial-type of documentation here: https://github.com/videlalvaro/RabbitMqBundle
It helped me get going with rabbitmq in symfony.
In a nutshell:
1: You need to have some configration in the config.yml-file. For example:
# RabbitMQ Configuration
old_sound_rabbit_mq:
connections:
default:
host: 'localhost'
port: 5672
user: 'guest'
password: 'guest'
vhost: '/'
lazy: true
connection_timeout: 3
read_write_timeout: 3
# requires php-amqplib v2.4.1+ and PHP5.4+
keepalive: false
# requires php-amqplib v2.4.1+
heartbeat: 0
producers:
task_example:
connection: default
exchange_options: {name: 'task_example', type: direct}
consumers:
task_example:
connection: default
exchange_options: {name: 'task_example', type: direct}
queue_options: {name: 'task_example'}
callback: test_class
Here the connection is defined, and one producer and one consumer. Both use the same "default" connection.
You will also need to define the callback as a service:
# My services
services:
test_class:
class: AppBundle\Testclasses\rabbittest\testclass
arguments: [#logger]
2: Now you need to have the consumer, which is the callback-option here, the "test_class". Simple consumer could look like this:
namespace AppBundle\Testclasses\rabbittest;
use OldSound\RabbitMqBundle\RabbitMq\ConsumerInterface;
use PhpAmqpLib\Message\AMQPMessage;
class testclass implements ConsumerInterface
{
private $logger; // Monolog-logger.
// Init:
public function __construct( $logger )
{
$this->logger = $logger;
echo "testclass is listening...";
}
public function execute(AMQPMessage $msg)
{
$message = unserialize($msg->body);
$userid = $message['userid'];
// Do something with the data. Save to db, write a log, whatever.
}
}
3: And now the producer that you already had:
$msg = array('userid' => $someid);
$this->get('old_sound_rabbit_mq.task_example_producer')->publish(serialize($msg));
4: And final piece of the puzzle is running the consumer. Consumer is started from the console, I was developing in a windows machine, and used Windows PowerShell. You can start up the consumer like this:
php app/console rabbitmq:consumer task_example
And it should give you the text:
testclass is listening...
, if you copied that from this example. That text is not necessary, and without it, the console will output nothing but will work just fine. Unless some error is presented.
But remember that you have to be in the directory where your symfony-application is. For example:
C:\wamp\www\symfony\my_project
A queue is a list of messages you want processed.
An exchange is a router of messages to queues. (you can have multiple queues listing to the same exchange, for example).
A producer pushes messages to an exchange.
A consumer reads messages from the queue.
Normally you have one producer and many consumers to process the messages in parallel.
The code you posted demonstrates a producer publishing to an exchange.
RabbitMQBundle expects you to have in-depth knowledge of the broker internals. That's not always what you want.
There is a solution that hides all those nitty-gritty details, leaving a simple but yet powerful interface for you to use. The doc is short. If you follow it you get working solution with zero knowledge of how RabbitMQ actually works.
P.S. Here's the blog post on how to migrate from RabbitMQBundle to EnqueueBundle.

Difficulty confirming Instant Payment Notification (IPN) from Paypal within an MVC3 Application

I am trying to get Paypal's IPN service working within my app.
When I use the Paypal Sandbox IPN Simulator set to the transaction type of, "Web Accept," Paypal says the message went through just fine (and if I mess up the code in my Action that handles the IPN, Paypal says there was a server error, so this seems to be communicating correctly).
However, it doesn't appear to actually be doing anything. If I navigate to my IPN action in a browser myapp.com/Paypal/IPN, I receive a response from paypal that says INVALID (as expected) and this is written to my output via Debug.Write. When I click "Send IPN" in Paypal's simulator, I get no debug messages at all, although my IPN action is full of Debug.Write lines. (Do calls made from outside your local environment simply not allow Debug.Write output?)
For reference, here is the majority of my IPN Action (I've removed various logic for clarity's sake):
public ActionResult IPN()
{
Debug.Write("entering ipn action ");
var formVals = new Dictionary<string, string>();
formVals.Add("cmd", "_notify-validate");
string response = GetPayPalResponse(formVals, true);
Debug.Write("IPN Response received: " + response + " <-- That was response. . . ");
if (response == "VALID")
{
Debug.Write("Response Was Verified");
}
else
{
Debug.Write("RESPONSE WAS NOT VERIFIED");
}
return this.View();
}
string GetPayPalResponse(Dictionary<string, string> formVals, bool useSandbox)
{
string paypalUrl = useSandbox
? "https://www.sandbox.paypal.com/cgi-bin/webscr"
: "https://www.paypal.com/cgi-bin/webscr";
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(paypalUrl);
//Set values for the request back
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
byte[] param = Request.BinaryRead(Request.ContentLength);
string strRequest = Encoding.ASCII.GetString(param);
StringBuilder sb = new StringBuilder();
sb.Append(strRequest);
foreach (string key in formVals.Keys)
{
sb.AppendFormat("&{0}={1}", key, formVals[key]);
}
strRequest += sb.ToString();
req.ContentLength = strRequest.Length;
string response = "";
using (StreamWriter streamOut = new StreamWriter(req.GetRequestStream(), System.Text.Encoding.ASCII))
{
streamOut.Write(strRequest);
streamOut.Close();
using (StreamReader streamIn = new StreamReader(req.GetResponse().GetResponseStream()))
{
response = streamIn.ReadToEnd();
}
}
return response;
}
Am I correct in understanding that if Paypal is actually submitting a request to my IPN action, I should receive the Debug.Write messages the same as when I visit the IPN action within my browser?
It does not appear to me that anything actually happens when Paypal sends the IPN simulated message to my web application, but Paypal says things are ok and Paypal somehow knows if I intentionally make the IPN action have an error when it is caused (so it appears to actually be calling the action somehow).
Can anyone help me understand what I am not understanding here?
I just want my user's to be able to pay using Paypal's standard payment method with a 'buy now' button and be able to change a database value from false to 'true' when the payment is confirmed to have been received.
Thank you for your time.
Note: Another way I tested this was to have the action change something in my database if it was called (I simply did something like MyEntity.Value = new value then db.SaveAll();. This change to my database was made if I navigated directly to the action within my browser, but no change occurs when I had the paypal IPN simulator 'ping' the action.
Update:
Ok, running trace using trace.axd:
<trace enabled="true" requestLimit="100" pageOutput="false" traceMode="SortByTime" localOnly="false" />
It behaves as if nothing happens when I run the Paypal IPN Simulator or if I browse to my web page using a device that is off my local network.
Note that I do see details change when I visit the pages on my local computer:
8 7/8/2012 6:25:01 PM paypal/ipn 200 GET View Details
9 7/8/2012 6:25:02 PM paypal/themes/ui-lightness/jquery-ui-1.8.19.custom.css 404 GET View Details
10 7/8/2012 6:25:02 PM favicon.ico 404 GET View Details
11 7/8/2012 6:25:52 PM favicon.ico 404 GET View Details
12 7/8/2012 6:26:09 PM home/paypaltest 200 GET View Details
Update 2:
I got the debugger to start debugging by attaching the debugger to : w3wp.exe
It appears that Paypal is indeed making it to my page and responding with VERIFIED at this point when I use the IPN simulator. Not sure what this means for me. Will update.
Update 3:
With debugging working I was able to properly test everything and the IPN verification was actually working as intended - I just couldn't tell without proper debugging messages.
Thank you for your time.
With debugging working I was able to properly test everything and the IPN verification was actually working as intended - I just couldn't tell without proper debugging messages.
Thank you for your time.

Resources