This might seem like a duplicate question but I wouldn't be here if any of the proposed solutions did work. I have been trying to integrate PayPal IPN on my server-side according to their docs and the Github code sample here https://github.com/paypal/ipn-code-samples/blob/master/python/paypal_ipn.py
I have changed the encoding of my sandbox business account to UTF-8 as per this answer Paypal sandbox IPN return INVALID but I keep getting the same INVALID response.
I am using python with Django REST framework. Here's my code.
class PaypalWebhookAPIView(APIView):
permission_classes = (AllowAny,)
def post(self, request, *args, **kwargs):
VERIFY_URL_PROD = 'https://ipnpb.paypal.com/cgi-bin/webscr'
VERIFY_URL_TEST = 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr'
# Switch as appropriate
VERIFY_URL = VERIFY_URL_TEST
data = {
'cmd': '_notify-validate',
**request.data
}
# Post back to PayPal for validation
headers = {'content-type': 'application/x-www-form-urlencoded',
'user-agent': 'Python-IPN-Verification-Script'}
response = requests.post(VERIFY_URL, data=data,
headers=headers)
response.raise_for_status()
import pdb
pdb.set_trace()
# Check return message and take action as needed
if response.text == 'VERIFIED':
pass
elif response.text == 'INVALID':
pass
else:
pass
This is the IPN message I received. You will notice that when checking out I added a custom_id field.
{'id': 'WH-8VT506292F0529107-6AL76556YR820754B', 'event_version': '1.0', 'create_time': '2020-02-10T05:44:14.774Z', 'resource_type': 'capture', 'resource_version': '2.0', 'event_type': 'PAYMENT.CAPTURE.COMPLETED', 'summary': 'Payment completed for $ 30.0 USD', 'resource': {'id': '5YA69751S2248772K', 'amount': {'currency_code': 'USD', 'value': '30.00'}, 'final_capture': True, 'seller_protection': {'status': 'ELIGIBLE', 'dispute_categories': ['ITEM_NOT_RECEIVED', 'UNAUTHORIZED_TRANSACTION']}, 'seller_receivable_breakdown': {'gross_amount': {'currency_code': 'USD', 'value': '30.00'}, 'paypal_fee': {'currency_code': 'USD', 'value': '1.32'}, 'net_amount': {'currency_code': 'USD', 'value': '28.68'}}, 'custom_id': 'RO8ZXX20', 'status': 'COMPLETED', 'create_time': '2020-02-10T05:44:10Z', 'update_time': '2020-02-10T05:44:10Z', 'links': [{'href': 'https://api.sandbox.paypal.com/v2/payments/captures/5YA69751S2248772K', 'rel': 'self', 'method': 'GET'}, {'href': 'https://api.sandbox.paypal.com/v2/payments/captures/5YA69751S2248772K/refund', 'rel': 'refund', 'method': 'POST'}, {'href': 'https://api.sandbox.paypal.com/v2/checkout/orders/5GN0468041100792N', 'rel': 'up', 'method': 'GET'}]}, 'links': [{'href': 'https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-8VT506292F0529107-6AL76556YR820754B', 'rel': 'self', 'method': 'GET'}, {'href': 'https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-8VT506292F0529107-6AL76556YR820754B/resend', 'rel': 'resend', 'method': 'POST'}]}
This is the data I am sending back.
{'cmd': '_notify-validate', 'id': 'WH-8VT506292F0529107-6AL76556YR820754B', 'event_version': '1.0', 'create_time': '2020-02-10T05:44:14.774Z', 'resource_type': 'capture', 'resource_version': '2.0', 'event_type': 'PAYMENT.CAPTURE.COMPLETED', 'summary': 'Payment completed for $ 30.0 USD', 'resource': {'id': '5YA69751S2248772K', 'amount': {'currency_code': 'USD', 'value': '30.00'}, 'final_capture': True, 'seller_protection': {'status': 'ELIGIBLE', 'dispute_categories': ['ITEM_NOT_RECEIVED', 'UNAUTHORIZED_TRANSACTION']}, 'seller_receivable_breakdown': {'gross_amount': {'currency_code': 'USD', 'value': '30.00'}, 'paypal_fee': {'currency_code': 'USD', 'value': '1.32'}, 'net_amount': {'currency_code': 'USD', 'value': '28.68'}}, 'custom_id': 'RO8ZXX20', 'status': 'COMPLETED', 'create_time': '2020-02-10T05:44:10Z', 'update_time': '2020-02-10T05:44:10Z', 'links': [{'href': 'https://api.sandbox.paypal.com/v2/payments/captures/5YA69751S2248772K', 'rel': 'self', 'method': 'GET'}, {'href': 'https://api.sandbox.paypal.com/v2/payments/captures/5YA69751S2248772K/refund', 'rel': 'refund', 'method': 'POST'}, {'href': 'https://api.sandbox.paypal.com/v2/checkout/orders/5GN0468041100792N', 'rel': 'up', 'method': 'GET'}]}, 'links': [{'href': 'https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-8VT506292F0529107-6AL76556YR820754B', 'rel': 'self', 'method': 'GET'}, {'href': 'https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-8VT506292F0529107-6AL76556YR820754B/resend', 'rel': 'resend', 'method': 'POST'}]}
This is the IPN message I received
You did not receive an IPN. You received a Webhook event notification. Webhooks do not use postbacks for validation, but rather a signature in the event's header.
Instant Payment Notification (IPN) -- which uses postbacks to the ipnpb host with &cmd=_notify-validate for validation -- is a much older service that predates the existence of Webhooks.
Related
How to disable sound when FCM is send, I want to send FCM without sound..
I'm able right now to send succesfuly FCM with sound
var body = jsonEncode(<String, dynamic>{
'notification': <String, dynamic>{
"body": userModel.userName +' '+ 'Followed you',
// 'title': "${userModel.userName}"
'title':'',
'sound':""
},
'priority': 'high',
'data': <String, dynamic>{
'click_action': 'FLUTTER_NOTIFICATION_CLICK',
'id': '1',
'status': 'done',
"type": NotificationType.Follow.toString(),
"senderId": userModel.userId,
"title": "title",
"body": userModel.userName +' '+ 'Followed you',
"postId": ""
},
'to': userModel.userName
});
body: body);
print(response.body.toString());
}
Hi I'm creating a Rider app I want to push a Notification and show alert dialog to Rider when a Delivery Request is placed for him in Rest API, Delivery Request is coming from Rest API but I'm unable to show Notification to Rider . If any one have done this before or have any Idea please Guide.
Thanks in Advance.
use the following code:
http.post(
'https://fcm.googleapis.com/fcm/send',
headers: <String, String>{
'Content-Type': 'application/json',
'Authorization': 'key=$serverToken',
},
body: jsonEncode(
<String, dynamic>{
'notification': <String, dynamic>{
'body': 'this is a body',
'title': 'this is a title'
},
'priority': 'high',
'data': <String, dynamic>{
'click_action': 'FLUTTER_NOTIFICATION_CLICK',
'id': '1',
'status': 'done'
},
'to': deviceToken,
},
),
);
you can find server token in your firebase console->Project settings->Cloud messaging tab->server key
and device token is found after installing firebase_messaging: package and
FirebaseMessaging fcm=FirebaseMessaging();
print(fcm.getToken());//deviceToken
I can send an FCM message to my Flutter app when the app has subscribed to "test_fcm_topic" as a topic. But if I subscribe to anything else IE: "redrobin" I don't receive the notification. I've tried sending from both the Flutter app and from Postman. In both cases the terminal shows the instance is received but there is no sound or notification popup.
I'm completely baffled as to why I cannot change the topic to anything other than "test_fcm_topic". Why would it work with one topic but not in the other? How can I even begin to troubleshoot?
Here's the code I use to subscribe;
FCMConfig.init(onBackgroundMessage: firebaseMessagingBackgroundHandler).then((value) {FCMConfig.subscribeToTopic("test_fcm_topic");});
Here's the send code in Flutter;
void send() async {
await http.post(
'https://fcm.googleapis.com/fcm/send',
headers: <String, String>{
'Content-Type': 'application/json',
'Authorization': 'key=$serverToken',
},
body: jsonEncode(
<String, dynamic>{
'notification': <String, dynamic>{
'body': 'This is a body',
'title': 'Banana'
},
'priority': 'high',
'data': <String, dynamic>{
"key_1" : "Value for key_1",
"key_2" : "Value for key_2"
},
'to': '/topics/test_fcm_topic',
},
),
);
}
For Postman I use these key pairs in the headers
Key: Authorization Value: key= server key
Key: Content-Type: Value: application/json
And this is the Raw JSON Body;
{
"to" : "/topics/test_fcm_topic",
"collapse_key" : "type_a",
"notification" : {
"body" : "Body of Your Notification",
"title": "Banana"
},
"data" : {
"body" : "This is a body",
"title": "Title of Your Notification in Title",
"key_1" : "Value for key_1",
"key_2" : "Value for key_2"
}
}
Using your code like this works like a charm when testing on my device:
void send(String serverToken) async {
Response response = await post(
Uri.parse('https://fcm.googleapis.com/fcm/send'),
headers: <String, String>{
'Content-Type': 'application/json',
'Authorization':'key=$serverToken'
},
body: jsonEncode(
<String, dynamic>{
'notification': <String, dynamic>{'body': 'This is a body', 'title': 'Banana'},
'priority': 'high',
'data': <String, dynamic>{'audioid': '139', 'title': 'done all over time', 'name': 'Greengirl'},
'to': '/topics/hi',
},
),
);
print(response.body);
}
But please note, that you must subscribe to the topic 'hi3' used in this example.
By running :
FirebaseMessaging.instance.subscribeToTopic('hi3'); on the client you want to receive these broadcasts.
Output of the function above is:
I/flutter (18090): {"message_id":1225534686323630021}
followed by:
D/FLTFireMsgReceiver(18090): broadcast received for message
They even run faster than firebase console push notifications.
I'm integrating PayPal Plus, successfully on my site, except "Pay upon Invoice".
Here's my JavaScript code for integrating the payment wall:
<script src="https://www.paypalobjects.com/webstatic/ppplus/ppplus.min.js"
type="text/javascript"></script>
<script type="application/javascript">
var ppp = PAYPAL.apps.PPP({
"approvalUrl": "<?= $createPaymentArr['links'][1]['href']; ?>",
"placeholder": "ppplus",
"language": "de_DE",
"mode": "sandbox",
"showPuiOnSandbox": "true",
"country": "DE"
});
</script>
When I try to use "Pay upon Invoice" I get this error message:
"Unfortunately we can not process your purchase."
My API call:
$fields = '{
"intent":"sale",
"redirect_urls":{
"return_url":"http://XYZ.info/ABC/paypal/index.php",
"cancel_url":"http://XYZ.info/ABC/paypal/index.php"
},
"payer":{
"payment_method":"paypal"
},
"transactions":[
{
"amount":{
"total":"7.47",
"currency":"EUR",
"details":{
"subtotal":"7.41",
"tax":"0.03",
"shipping":"0.03"
}
},
"item_list":{
"items":[
{
"quantity":"1",
"name":"XYZ",
"price":"7.41",
"currency":"EUR"
}
],
"shipping_address":{
"line1":"XYZ Straße 587",
"city":"OPA",
"postal_code":"12345",
"country_code":"DE"
}
},
"description":"XYZ.info Warenkorb"
}
]
}';
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://api".$mode.".paypal.com/v1/payments/payment",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => $fields,
CURLOPT_HTTPHEADER => array(
"Authorization: Bearer ".$arr['access_token'],
"Content-Type: application/json"
),
));
You need to patch the payment before showing the iframe.
During the patch, simply supply the shipping address.
Then payment upon invoice works, even in the sandbox.
I'm attempting to integrate my webapp's calendar with Google Calendar. I'm successfully authenticating the user, able to read events from Google and delete events from Google. I'm having trouble updating events from my app to Google.
I'm using the PATCH method described here as I only want to modify the fields that I send in the API call (as opposed to the PUT call which requires all fields for the event).
My Perl code create a JSON object representing what I want to change. I then submit a PATCH request to Google, and I receive status 200 OK and the event resource back from Google. What I receive back is supposed to be the modified event resource, but the fields I have modified are not modified in the response. When I check in Google Calendar itself, once again I see the event is not modified.
I'm confused as to why I'm getting the 200 OK response instead of an error, when the event is not updating. Curiously, in the response, the "updated" timestamp property has been updated, but the summary field has not.
my $ua = LWP::UserAgent->new;
my $url = "https://www.googleapis.com/calendar/v3/calendars/primary/events/${id}?access_token=${access_token}";
my $resource{"summary"} = "$g{summary}";
$resource = encode_json(\%resource);
my $headers = HTTP::Headers->new();
$headers->header(If_Match => "$etag");
my $request = HTTP::Request->new('PATCH',$url,$headers,$resource);
my $response = $ua->request($request);
my $status = $response->status_line;
my $result = decode_json($response->decoded_content);
The "etag" value from the result is updated as I expect, which indicates that the calendar event is being (somewhat) modified in Google.
Some Data::Dumper tracing:
The request:
$VAR1 = bless( {
'_content' => '{"summary":"End of season function MODIFIED"}',
'_uri' => bless( do{\(my $o = 'https://www.googleapis.com/calendar/v3/calendars/primary/events/<eventID>?access_token=<access_token>')}, 'URI::https' ),
'_headers' => bless( {
'if-match' => '<etag>'
}, 'HTTP::Headers' ),
'_method' => 'PATCH'
}, 'HTTP::Request' );
The response:
$VAR1 = bless( {
'_protocol' => 'HTTP/1.1',
'_content' => '{
"kind": "calendar#event",
"etag": "<etag>",
"id": "<eventID>",
"status": "confirmed",
"htmlLink": "<suppressed>",
"created": "2012-03-08T05:06:04.000Z",
"updated": "2012-03-25T09:18:49.000Z",
"summary": "End of season function",
"creator": {
"email": "<suppressed>"
},
"organizer": {
"email": "<suppressed>"
},
"start": {
"date": "2012-03-24"
},
"end": {
"date": "2012-03-24"
},
"iCalUID": "<suppressed>",
"sequence": 1,
"reminders": {
"useDefault": true
}
}
',
'_rc' => '200',
'_headers' => bless( {
'connection' => 'close',
'cache-control' => 'no-cache, no-store, max-age=0, must-revalidate',
'date' => 'Sun, 25 Mar 2012 09:18:49 GMT',
'client-ssl-cert-issuer' => '/C=US/O=Google Inc/CN=Google Internet Authority',
'client-ssl-cipher' => 'ECDHE-RSA-RC4-SHA',
'client-peer' => '173.194.72.95:443',
'client-date' => 'Sun, 25 Mar 2012 09:18:49 GMT',
'pragma' => 'no-cache',
'content-type' => 'application/json; charset=UTF-8',
'x-xss-protection' => '1; mode=block',
'server' => 'GSE',
'client-ssl-socket-class' => 'IO::Socket::SSL',
'client-response-num' => 1,
'etag' => '<etag>',
'x-frame-options' => 'SAMEORIGIN',
'x-content-type-options' => 'nosniff',
'client-ssl-cert-subject' => '/C=US/ST=California/L=Mountain View/O=Google Inc/CN=*.googleapis.com',
'expires' => 'Fri, 01 Jan 1990 00:00:00 GMT'
}, 'HTTP::Headers' ),
'_msg' => 'OK',
'_request' => bless( {
'_content' => '{"summary":"End of season function MODIFIED"}',
'_uri' => bless( do{\(my $o = 'https://www.googleapis.com/calendar/v3/calendars/primary/events/<eventID>?access_token=<access_token>')}, 'URI::https' ),
'_headers' => bless( {
'user-agent' => 'libwww-perl/6.01',
'if-match' => '<etag>'
}, 'HTTP::Headers' ),
'_method' => 'PATCH',
'_uri_canonical' => $VAR1->{'_request'}{'_uri'}
}, 'HTTP::Request' )
}, 'HTTP::Response' );
Can anyone see what I'm doing wrong? I'm sure that Google is accepting my PATCH request, but for some reason not exactly seeing the field I want to modify. I've tried submitting the exact same JSON object on their testing page and have no problem getting that to work...
I found the solution purely by trial and error - adding the "Content-Type" header to the HTTP header seemed to do the trick:
my $headers = HTTP::Headers->new();
$headers->header(If_Match => "$etag");
$headers->header(Content_Type => "application/json");
Adding the header accept = application/json fixed it for me