I am trying to understand the flow for a payment using Omnipay/SecurePay but always get an error when trying to complete the purchase.
From what I can see from the online docs the completePurchase function should be called with the same params as the purchase function but when I call completePurchase I receive an "Invalid fingerprint" exception.
Also these errors are being thrown :
Undefined index: merchant in /var/www/vendor/omnipay/securepay/src/Message/DirectPostCompletePurchaseRequest.php on line 28
Undefined index: refid in /var/www/vendor/omnipay/securepay/src/Message/DirectPostCompletePurchaseRequest.php on line 30
Undefined index: timestamp in /var/www/vendor/omnipay/securepay/src/Message/DirectPostCompletePurchaseRequest.php on line 32
Undefined index: summarycode in /var/www/vendor/omnipay/securepay/src/Message/DirectPostCompletePurchaseRequest.php on line 33
Am I missing a step somewhere that adds this missing data? or should this data be coming back in the response?
Code:
$params = array(
'amount' => $data->payment['amount'] . '.00',
'currency' => $this->getOptions()->getCurrency(),
'description' => 'test purchase',
'transactionId' => '12345',
'transactionReference' => $data->course['course_code'],
'returnUrl' => 'http://test.localhost/register/55622/confirmation',
'cancelUrl' => 'http://test.localhost/register/55622/summary',
'card'=>$card
);
$gateway = new DirectPostGateway();
$gateway->setMerchantId( $this->getOptions()->getGateway( $type )['merchant_id'] );
$gateway->setTransactionPassword( $this->getOptions()->getGateway( $type )['password'] );
$gateway->setTestMode( $this->getOptions()->getTestMode() );
$response = $gateway->purchase($params)->send();
var_dump($response->getRedirectData());
$response = $gateway->completePurchase($params)->send();
var_dump($response);
//"Invalid fingerprint" exception thrown
if ($response->isSuccessful()) {
// payment was successful: update database
return $response;
} elseif ($response->isRedirect()) {
// redirect to offsite payment gateway
if($response->getRedirectData()){
var_dump($response->getRedirectData());
} else {
return $response->redirect();
}
exit;
return $response->redirect();
} else {
// payment failed: display message to customer
// echo $response->getMessage();
throw new Exception("Error Processing Request", 1);
}
You are doing things correctly. When SecurePay returns to your website, there should be POST data containing those parameters, as well as the fingerprint parameter which confirms the authenticity of the request.
I would watch the Network tab of your browser while making a payment with SecurePay, and check the HTTP POST data after payment is complete (and redirecting to your site). My guess is that some htaccess or other script is making a second redirect, and stripping the important POST data at the same time.
Omnipay will automatically check the POST data, so there is no need to explicitly send it through. As long as you call completePurchase() from the same request it should process the payment correctly.
See: https://github.com/omnipay/securepay/blob/master/src/Message/DirectPostCompletePurchaseRequest.php
Securepay was using endpoint https://test.securepay.com.au and then switchign to https://test.api.securepay.com.au
Related
I'm integrating a CRM with Facebook lead Ads using Zapier, and I can create a lead in Facebook and create it in the CRM without any issues.
After a successful post (i.e. successfully creating the lead), I'm curious what I should be returning, I would have thought
return Ok();
would have been enough. Instead, I get an error message saying:
- Got a non-object result, expected an object from create ()
What happened (You are seeing this because you are an admin):
Executing creates.ZapLead.operation.perform with bundle
Invalid API Response:
- Got a non-object result, expected an object from create ()
What should I be returning?
Code which makes the post is:
perform: (z, bundle) => {
const promise = z.request({
url: 'https://{{bundle.authData.subdomain}}.ngrok.io/api/zapier/create/lead/' + bundle.inputData.lead_type + '/' + bundle.inputData.core_customerTypeId,
method: 'POST',
body: JSON.stringify({
fields: bundle.inputData
}),
headers: {
'content-type': 'application/json'
}
});
// return promise.then((response) => JSON.parse(response.content));
return promise.then((response) => {
if (response.status != 200) {
throw new Error(`Unexpected status code ${response.status}`);
}
});
Any ideas?
Thanks,
David here, from the Zapier Platform team.
While your answer is technically correct, I wanted to add some context about the error and why your solution works.
Zapier expects a javascript object (basically anything valid and wrapped in {}) to come out of a create function. That's why JSON.parse(response.content) works, it's returning whatever the server said. Your code throws an error if it's not a 200, but doesn't return anything if it is a 200. Since undefined is not of type Object, the error you're seeing is thrown.
While {"Success":"Success","Attempt":null,"Id":null,"RequestId":null} is totally a valid response (it's an object!), it's more useful for the end-user to return data about the new lead that was created. That way, it can be used downstream for other actions.
Let me know if you've got any other questions!
As a side note, we're very open to how to make that error message more clear; it's one devs struggle with a lot.
I think I found it. Looks like I need to return this if successful:
{"Success":"Success","Attempt":null,"Id":null,"RequestId":null}
I am using twilio to send bulk sms messages. Let's say some customer decided that they don't want to receive messages anymore so they reply with "stop" and that will add them to the black list. I am hard coding the phone numbers because I am still testing on my own cell phones. I noticed that when I do not remove the numbers on the black list from my code, I am getting an error message and my script stops at that point.
In the future, I will probably be using numbers stored in a database or a file. In that case, how do I overcome this problem if it happened. Basically what I want to do is: If a number is in the black list, move on to the next number and avoid that error using an exception or something.
The error message and code is below.
Thanks,
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Send SMS</title>
<?php
/* Send an SMS using Twilio. You can run this file 3 different ways:
*
* 1. Save it as sendnotifications.php and at the command line, run
* php sendnotifications.php
*
* 2. Upload it to a web host and load mywebhost.com/sendnotifications.php
* in a web browser.
*
* 3. Download a local server like WAMP, MAMP or XAMPP. Point the web root
* directory to the folder containing this file, and load
* localhost:8888/sendnotifications.php in a web browser.
*/
// Step 1: Get the Twilio-PHP library from twilio.com/docs/libraries/php,
// following the instructions to install it with Composer.
//require_once "vendor/autoload.php";
require __DIR__ . '/twilio-php-master/Twilio/autoload.php';
use Twilio\Rest\Client;
// Step 2: set our AccountSid and AuthToken from https://twilio.com/console
$AccountSid = "something";
$AuthToken = "something";
// Step 3: instantiate a new Twilio Rest Client
$client = new Client($AccountSid, $AuthToken);
// Step 4: make an array of people we know, to send them a message.
// Feel free to change/add your own phone number and name here.
$people = array(
"+17570123456" => "Chris",
"+17571234568" => "Hussam"
);
// Step 5: Loop over all our friends. $number is a phone number above, and
// $name is the name next to it
foreach ($people as $number => $name) {
$sms = $client->account->messages->create(
// the number we are sending to - Any phone number
$number,
array(
// Step 6: Change the 'From' number below to be a valid Twilio number
// that you've purchased
'from' => "+184444444444",
// the sms body
'body' => "Hey $name, this is Hussam. Testing Twilio SMS API!"
)
);
// Display a confirmation message on the screen
echo "Sent message to $name.\n";
}
?>
( ! ) Fatal error: Uncaught exception 'Twilio\Exceptions\RestException' with message '[HTTP 400] Unable to create record: The message From/To pair violates a blacklist rule.' in C:\wamp64\www\Twilio\twilio-php-master\Twilio\Version.php on line 86 ( ! ) Twilio\Exceptions\RestException: [HTTP 400] Unable to create record: The message From/To pair violates a blacklist rule. in C:\wamp64\www\Twilio\twilio-php-master\Twilio\Version.php on line 86 Call Stack
Time Memory Function Location 1 0.0000 239280 {main}( ) ...\send.php:0 2 0.0156 799016 Twilio\Rest\Api\V2010\Account\MessageList->create( ) ...\send.php:56 3 0.0156 814688 Twilio\Version->create( ) ...\MessageList.php:63
Twilio developer evangelist here.
You need to catch the exception that is thrown from the request to send a message to the blacklisted number. You can do so with try and catch like this:
foreach ($people as $number => $name) {
try {
$sms = $client->account->messages->create(
$number,
array(
'from' => "+18443949780",
'body' => "Hey $name, this is Hussam. Testing Twilio SMS API!"
)
);
echo "Sent message to $name.\n";
} catch (\Twilio\Exceptions\RestException $e) {
echo "Couldn't send message to $number\n";
}
}
When you hook this up to a database, you'll want to use the catch to update a field to mark the number as blocked so that you don't try to send to it again.
Let me know if that helps at all.
This worked for me with Laravel 5. Notice the use of \Twilio\Exceptions\RestException.
try {
$sms = $client->account->messages->create(
$number,
array(
'from' => "+16136543180",
'body' => "Hey $name, Are you still mad at us about your cat!"
)
);
echo "Sent message to $name.\n";
} catch (\Twilio\Exceptions\RestException $e) {
if ($e->getCode() == 20404) {
//this will be false condition
dd('False Result 404');
} else {
//some other exception code
dd($e->getMessage());
}
}
I have two subdomains: https://abc.xxxx.com and https://xyz.xxxx.com. So my questions:
1). is it possible to register a service worker for
https://xyz.xxxx.com from https://abc.xxxx.com ? if yes then how?
2). if http://abc.xxxx.com (http insecure) then anyway to register
a service worker for https://xyz.xxxx.com from http://abc.xxxx.com like in iframe or something....
This is a real situation, I am facing for my multiple subdomain. Any help appreciated. Thanks in advance.
Here are some general answers that I think should address the various points you raise in your question:
Each registered service worker has an associated scope, which dictates the set of web pages that the service worker can control. The scope of a service worker is a URL, and that URL must have the same origin as the page that registers the service worker, and must be either a URL that corresponds to the same path level as the page or a path that's one or more levels down. The default scope corresponds to the same path level as location of the service worker script. Because of this restriction, it's not possible to call navigator.serviceWorker.register(...) from a page on one (sub-)domain and end up with a service worker that controls pages on another (sub-)domain.
There are restrictions in place to prevent you from throwing an https: <iframe> on an http: page and using that to register a service worker. See DOMException when registering service worker inside an https iframe
Though I don't know that it's directly related to your question, explicitly calling fetch() for an http: resource within your service worker code will result in a failure in current versions of Chrome, since mixed-content fetch()s are not allowed within a service worker. I don't know if things are 100% settled on that front, and this open bug is still relevant.
If you have pages that live on both abc.123.com and xyz.123.com and you want both sets of pages to be controlled by a service worker, then you need to have two separate service worker registrations. Each registration needs to be for a copy of your service worker JS file that's hosted on the respective domain corresponding to the top-level page, and all pages and service worker scripts need to be accessed via https:.
That being said, you can kick off a service worker registration for a different domain by including a cross-domain <iframe> on a page, but both the host page and the <iframe> need to be served via https:. The normal service worker scoping restrictions apply, so if, for example, you want to register a service worker for the other domain that will cover the entire https://other-domain.com/ scope, you need to make sure that the location of the service worker script being registered is at the top-level, e.g. https://other-domain.com/service-worker.js, not at https://other-domain.com/path/to/service-worker.js. This is the approach used by, for example, the AMP project via the <amp-install-serviceworker> element.
Service Worker scripts must be hosted at the same origin (Protocol + Domain name + Port). Each sub-domain is considered a different origin, So, you will need to register a service worker for each one. Each of these workers will have its own cache and scope.
Try use Ngnix proxy_pass. This work for me.
My bad, I misunderstood a bit. Well, here's the code
if('serviceWorker' in navigator){
if(window.location.pathname != '/'){
//register with API
if(!navigator.serviceWorker.controller) navigator.serviceWorker.register('/service-worker', { scope: '/' });
//once registration is complete
navigator.serviceWorker.ready.then(function(serviceWorkerRegistration){
//get subscription
serviceWorkerRegistration.pushManager.getSubscription().then(function(subscription){
//enable the user to alter the subscription
//jquery selector for enabling whatever you use to subscribe.removeAttr("disabled");
//set it to allready subscribed if it is so
if(subscription){
//code for showing the user that they're allready subscribed
}
});
});
}
}else{
console.warn('Service workers aren\'t supported in this browser.');
}
then here's the event -ish for your subscribe / unsubscribe
// subscribe or unsubscribe to the ServiceWorker
$(document.body).on('change', /*selector*/, function(){
//new state is checked so we subscribe
if($(this).prop('checked')){
navigator.serviceWorker.ready.then(function(serviceWorkerRegistration){
serviceWorkerRegistration.pushManager.subscribe()
.then(function(subscription){
// The subscription was successful
console.log('subscription successful'); //subscription.subscriptionId
//save in DB - this is important because
$.post($('#basePath').val() + 'settings/ajax-SW-sub/', {id:subscription.subscriptionId}, function(data){
//console.log(data);
}, 'json');
}).catch(function(e) {
if (Notification.permission === 'denied') {
// The user denied the notification permission which
// means we failed to subscribe and the user will need
// to manually change the notification permission to
// subscribe to push messages
console.warn('Permission for Notifications was denied');
} else {
// A problem occurred with the subscription; common reasons
// include network errors, and lacking gcm_sender_id and/or
// gcm_user_visible_only in the manifest.
console.error('Unable to subscribe to push.', e);
}
});
});//*/
//new state us unchecked so we unsubscribe
}else{
$('.js-enable-sub-test').parent().removeClass('checked');
//get subscription
navigator.serviceWorker.ready.then(function(reg) {
reg.pushManager.getSubscription().then(function(subscription) {
//unregister in db
$.post($('#basePath').val() + 'settings/ajax-SW-unsub/', {id:subscription.subscriptionId}, function(data){
//console.log(data);
}, 'json');
//remove subscription from google servers
subscription.unsubscribe().then(function(successful) {
// You've successfully unsubscribed
console.log('unsubscribe successful');
}).catch(function(e) {
// Unsubscription failed
console.log('unsubscribe failed', e);
})
})
});//*/
}
});
after that you need to register an account on the google developer console and register a project for something like *.xxxx.com . Then you need to get a proper manifest json with gcm_sender_id and gcm_user_visible_only
You need to create a key for both server and browser applications, there's more info on that on this page.
https://developers.google.com/web/updates/2015/03/push-notificatons-on-the-open-web?hl=en
The one for browser applications goes in your manifest json.
Then to send out push notifications you'll be using something like this:
function addSWmessage($args){
$output = false;
if(!isset($args['expiration']) || $args['expiration'] == ''){
$args['expiration'] = date('Y-m-d H:i:s', strtotime('+7 days', time()));
}
$sql = sprintf("INSERT INTO `serviceworker_messages` SET title = '%s', body = '%s', imageurl = '%s', linkurl = '%s', hash = '%s', expiration = '%s'",
parent::escape_string($args['title']),
parent::escape_string($args['text']),
parent::escape_string($args['imageurl']),
parent::escape_string($args['linkurl']),
parent::escape_string(md5(uniqid('******************', true))),
parent::escape_string($args['expiration']));
if($id = parent::insert($sql)){
$output = $id;
}
return $output;
}
function pushSWmessage($args){
//$args['messageid'] $args['userids'][]
foreach($args['userids'] as $val){
$sql = sprintf("SELECT messages_mobile, messages FROM `users_serviceworker_hash` WHERE users_id = '%s'",
parent::escape_string($val));
if($row = parent::queryOne($sql)){
$m1 = json_decode($row['messages'], true);
$m1[] = $args['messageid'];
$m2 = json_decode($row['messages_mobile'], true);
$m2[] = $args['messageid'];
$sql = sprintf("UPDATE `users_serviceworker_hash` SET messages = '%s', messages_mobile = '%s' WHERE users_id = '%s'",
parent::escape_string(json_encode($m1)),
parent::escape_string(json_encode($m2)),
parent::escape_string($val['users_id']));
parent::insert($sql);
}
}
$sql = sprintf("SELECT subscriptionID, users_id FROM `users_serviceworker_subscriptions`");
if($rows = parent::query($sql)){
foreach($rows as $val){
if(in_array($val['users_id'], $args['userids'])){
$registrationIds[] = $val['subscriptionID'];
}
}
if(isset($registrationIds) && !empty($registrationIds)){
// prep the bundle
$msg = array
(
'message' => '!',
'title' => '!',
'subtitle' => '!',
'tickerText' => '!',
'vibrate' => 1,
'sound' => 1,
'largeIcon' => '!',
'smallIcon' => '!'
);
$headers = array
(
'Authorization: key='.SW_API_ACCESS_KEY,
'Content-Type: application/json'
);
$fields = array
(
'registration_ids' => $registrationIds,
'data' => $msg
);
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL, 'https://android.googleapis.com/gcm/send');
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch,CURLOPT_POSTFIELDS, json_encode($fields));
curl_exec($ch);
curl_close($ch);
}
}
}
And no, I don't know what issue you've been having but this works for me with multiple sub domains. :)
I am using Twilio Rest API to pull some usage logs
foreach ($client->account->usage_records->last_month as $record) {
if ($record->count > 0) {
$call_record[] = array(
'category' => $record->category,
'count' => $record->count,
'usage' => $record->usage,
'price'=>$record->price,
'price_unit'=>$record->price_unit
);
}
}
It is giving me this error:
Uncaught exception 'Services_Twilio_RestException' with message 'Could not decode response body as JSON. This likely indicates a 500 server error' in /Users/enmanuelcorvo/development/demos/twilio_usage/vendor/twilio/sdk/Services/Twilio.php:28
I tried wrapping the code inside a try catch like this
try {
// Loop over the list of records and echo a property for each one
foreach ($client->account->usage_records->last_month as $record) {
if ($record->count > 0) {
$call_record[] = array(
'category' => $record->category,
'count' => $record->count,
'usage' => $record->usage,
'price'=>$record->price,
'price_unit'=>$record->price_unit
);
}
}
} catch (Exception $e) {
echo($e->getMessage());
}
and I was able to get some usage data back but not all. I am missing things like calls-outbound which I know for sure we have some. So I think it breaks trying to fetch some categories and that is why it is trowing the error, but that is just a really wild guess. Any ideas what could be causing this?
Here is the stack-trace:
Stack trace:
**0 /Users/enmanuelcorvo/development/demos/twilio_usage/vendor/twilio/sdk/Services/Twilio.php(265): Base_Services_Twilio->_processResponse(Array)
1 /Users/enmanuelcorvo/development/demos/twilio_usage/vendor/twilio/sdk/Services/Twilio.php(236): Base_Services_Twilio->_makeIdempotentRequest(Array, '/2010-04-01/Acc...', 1)
2 /Users/enmanuelcorvo/development/demos/twilio_usage/vendor/twilio/sdk/Services/Twilio/ListResource.php(120): Base_Services_Twilio->retrieveData('/2010-04-01/Acc...', Array, true)
3 /Users/enmanuelcorvo/development/demos/twilio_usage/vendor/twilio/sdk/Services/Twilio/ListResource.php(179): Services_Twilio_ListResource->getPage(1, 50, Array, '/2010-04-01/Acc...')
4 [internal function]: Services_Twilio_ListResource->getPageGenerator(1, 50, in /Users/enmanuelcorvo/development/demos/twilio_usage/vendor/twilio/sdk/Services/Twilio.php on line 287**
Ross McKay has an article that suggest the issue is with the installation of PHP. As he puts it:
The error is caused by not having an up-to-date bundle of CA root certificates. This is typically a text file with a bunch of cryptographic signatures that curl uses to verify a host’s SSL certificate. You need to make sure that your installation of PHP has one of these files, and that it’s up to date.
Simply download the the latest CA root certificate bundle from here
And then within your php.ini file look for a section called [curl] (near the bottom in my case), and set your curl.cainfo according to where you place the cacert.pem, like so:
curl.cainfo=D:\Bitnami\wampstack\php\cacert.pem
Works like a charm!
Reference:
http://snippets.webaware.com.au/howto/stop-turning-off-curlopt_ssl_verifypeer-and-fix-your-php-config/
In my case the error was caused by an incorrect entry of the accountSID and AuthToken. Make sure they're formatted as follows:
$AccountSid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
$AuthToken = "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY";
Replace with your accountSid and AuthToken found at www.twilio.com/Console
I'm looking for a twimlet or php script for the Twilio API that would implement an outbound calling list function with optional transfer to recorded message -
Sales person Clicks number in a list to dial outbound call
dialed party answers
Sales person realizes it's a machine and clicks a link to transfer the call to a recorded message that will be left on the machine.
Or, if it's not a machine sales person proceeds with the call.
Is something like that available already canned or scripted?
Since the initial ask we've updated the click-to-call solution in a code complete tutorial.
In the last step, after a call is connected and Twilio asks for TwiML instructions you might consider adapting from the call screening tutorial to handle voicemail:
public function agentVoicemail(Request $request, $agentId)
{
$response = new Services_Twilio_Twiml;
$callStatus = $request->input('DialCallStatus');
if ($callStatus !== 'completed') {
$response->say(
'It appears that no agent is available. ' .
'Please leave a message after the beep',
['voice' => 'alice', 'language' => 'en-GB']
);
$response->record(
['maxLength' => '20',
'method' => 'GET',
'action' => route('hangup', [], false),
'transcribeCallback' => route(
'store-recording', ['agent' => $agentId], false
)
]
);
$response->say(
'No recording received. Goodbye',
['voice' => 'alice', 'language' => 'en-GB']
);
$response->hangup();
return $response;
}
return "Ok";
}