Stripe PHP Payment - php

Hi i'm trying to follow the stripe guide for an integrated subscription service, here's the code as follows
// Set your secret key. Remember to switch to your live secret key in production!
// See your keys here: https://dashboard.stripe.com/account/apikeys
\Stripe\Stripe::setApiKey('sk_test_51**********');
$app->post('/stripe-webhook', function(Request $request, Response $response) {
$logger = $this->get('logger');
$event = $request->getParsedBody();
$stripe = $this->stripe;
// Parse the message body (and check the signature if possible)
Im just wondering if anybody knows what // Parse the message body (and check the signature if possible) actually means.

If you expand the other 68 lines of that guide in this section [1] you'll see some example code that take the body of the POST request and passes it to the function to constructEvent(), effectively parsing the incoming JSON into an Event that you can use in your code.
The signature checking is an optional, but recommended way to make sure that the incoming message was actually sent by Stripe [2]. It works by passing the webhook secret (which you can enable/find in your Dashboard settings for webhooks) to the construct event function along with a signature which is sent as a header. If the sent and calculated (done inside of constructEvent() signatures do not match an exception will be raised.
[1] https://stripe.com/docs/billing/subscriptions/fixed-price#webhooks
[2] https://stripe.com/docs/webhooks/signatures

Related

DocuSign API: sending a signature request via e-mail with the Envelope ID

I am trying to do the following with the DocuSign API, but I am not getting very much wiser from their documentation.
The admin creates the envelope in DocuSign just as they normally would, but without sending it
They take the Envelope ID and enter it in our software
We show a button to the end user that sends out the Envelope signature request when clicked (based on the Envelope ID)
The closest I came to finding something like this was https://developers.docusign.com/docs/esign-rest-api/how-to/request-signature-email-remote/ but that doesn't seem to allow me to use and existing envelope.
The API reference doesn't seem to offer any help either (https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopes/)
What I have got so far is the following:
OAuth + generating the JWT + access token (works fine)
generating Recipient View (which is not what I need but needs to be replaced with the right call
$view_request = new \DocuSign\eSign\Model\RecipientViewRequest(['return_url' => $args['ds_return_url']]);
if ($args['starting_view'] == "envelope" && $args['envelope_id']) {
$view_request->setEnvelopeId($args['envelope_id']);
}
# Call the API method
$config = new \DocuSign\eSign\Configuration();
$config->setHost($args['base_uri']);
$config->addDefaultHeader('Authorization', 'Bearer ' . $args['ds_access_token']);
$api_client = new \DocuSign\eSign\Client\ApiClient($config);
$envelope_api = new \DocuSign\eSign\Api\EnvelopesApi($api_client);
$results = $envelope_api->createRecipientView($args['account_id'], $view_request);
$view_url = $results['url'];
return $view_url;
Thanks!
So based on your description it looks like for the first step, you're looking to create an envelope as a draft. Which is basically creating an envelope, filling all the information out, and then not sending it.
This will spit out an envelope Id which you can store in your application.
And when the button you describe is clicked, you can update the status of the envelope to "sent" using this endpoint which will send out the envelope.
If you're looking for something more detailed, you can always reach out to us at DocuSign Developer Support and we can discuss it further.

How to get the response on webhook URL after user sign the doc in docusign

I am redirecting user to a page where they can sign the doc and after sign I have set the return URL where it redirects the user.
Now how can I get the response sent from docusign.
I have tried $_REQUEST and $data = file_get_contents('php://input'); but didn't get the response. Is there any other way to get the response.
Once the user signs the embedded URL. The status of the envelop is changed. In your webApp you have to hook an eventNotification Object to the EnvelopDefinition Object which is used while envelope creation. In that particular eventNotification object you can specify the various statuses of the envelope on which you want a notification, you have to specify a call back endpoint. The call back end point can be configured on the url parameter present on the eventNotification Object.
Following is a java example of that eventConfiguration Object
EventNotification eventNotification = new EventNotification();
eventNotification.setUrl("https://exampleapp.com/api/envelope/status");
eventNotification.setIncludeCertificateOfCompletion(FALSE);
eventNotification.setIncludeDocuments(FALSE);
eventNotification.setRequireAcknowledgment(TRUE);
eventNotification.setUseSoapInterface(FALSE);
eventNotification.setLoggingEnabled(TRUE);
eventNotification.setEnvelopeEvents(getEnvelopeEvents());
return eventNotification;
Following is the configuration where you can specify to which state of envelope you want notification for. You can configure multiple states of the envelope.
private List<EnvelopeEvent> getEnvelopeEvents() {
List<EnvelopeEvent> envelopeEvents = new ArrayList<>();
EnvelopeEvent sentEnvelopeEvent = new EnvelopeEvent();
sentEnvelopeEvent.setEnvelopeEventStatusCode("sent");
sentEnvelopeEvent.setIncludeDocuments(FALSE);
envelopeEvents.add(sentEnvelopeEvent);
EnvelopeEvent completedEnvelopeEvent = new EnvelopeEvent();
completedEnvelopeEvent.setEnvelopeEventStatusCode("completed");
completedEnvelopeEvent.setIncludeDocuments(FALSE);
envelopeEvents.add(completedEnvelopeEvent);
return envelopeEvents;
}
And while creating envelope, just hook this object to the eventDefiniton Object which in turn will be passed to the createEnvelope method of EnvelopesApi Object.
EnvelopeDefinition envelopeDef = new EnvelopeDefinition();
envelopeDef.setEventNotification(eventNotification);
envelopesApi.createEnvelope(accountId, envelopeDef);
Once you get notification from docusign, you can update your contract status on database, or you can notify your ui about the envelope state change.

stripe webhook account.updated is not working

I am trying to send an (simple test) email when I receive an account.updated call for a custom account from Stripe API. My other webhooks to create charges and inform customers about successful or failed charges work like this, but here I get an error 500 (I can see that in the dashboard of the custom account) and the mail is NOT send, so I am not sure what I am doing wrong here. My code looks like this:
<?php
require_once('vendor/autoload.php');
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
\Stripe\Stripe::setApiKey("sk_test_XXXX");
// Retrieve the request's body and parse it as JSON
$input = #file_get_contents("php://input");
$event_json = json_decode($input);
// Verify the event by fetching it from Stripe
$event = \Stripe\Event::retrieve($event_json->id);
// Do something with $event
if ($event->type == 'account.updated') {
// The E-Mail message to send to inform about a succeeded charge
$message = 'test';
// Send the E-Mail
mail('test#example.com', 'We need more info about you!', $message);
}
http_response_code(200); // PHP 5.4 or greater
Thank you for your help!
If you look at your web server's error.log (usually accessible from your hosting control panel or in /var/log/) do you see more detail on what's causing the 500?
Could it be $event = \Stripe\Event::retrieve($event_json->id); failing?
If the event is occurring directly on a connected account, you may also need to pass the account id to retrieve it.
See here for a bit more context,
https://stripe.com/docs/connect/webhooks
The code would be more like:
$event = \Stripe\Event::retrieve(
array("id" => $event_json->id),
array("stripe_account" => $event_json->account));
https://stripe.com/docs/connect/authentication#authentication-via-the-stripe-account-header

How to verify Paypal webhook signature in PHP?

I'm not very knowledgeable in SSL and certificates. I used the post
"How to use hash_hmac() with "SHA256withRSA" on PHP?" to see if I can get webhooks with PayPal working.
The issue I am have is I am getting the following error after calling openssl_verify() and a return result of (0):
OpenSSL error openssl_verify error:04091068:rsa routines:INT_RSA_VERIFY:bad signature
I've tried to solve this, but documentation on errors and the functions around the web is minimal to none.
My current code looks like this:
// get the header post to my php file by PayPal
$headers = apache_request_headers();
// get the body post to me php file by PayPal
$body = #file_get_contents('php://input');
$json = json_decode($body);
// TransmissionId|TransmissionTimeStamp|WebhookId|CRC32 as per PayPal documentation
$sigString = $headers['Paypal-Transmission-Id'].'|'.$headers['Paypal-Transmission-Time'].'|'.$json->id.'|'.crc32($body);
// $headers['Paypal-Cert-Url'] contains the "-----BEGIN CERTIFICATE---MIIHmjCCBoKgAwIBAgIQDB8 ... -----END CERTIFICATE-----"
$pubKey = openssl_pkey_get_public(file_get_contents($headers['Paypal-Cert-Url']));
// and this is the call to verify that returns result (0)
$verifyResult = openssl_verify($sigString, base64_decode($headers['Paypal-Transmission-Sig']), $pubKey, 'sha256WithRSAEncryption');
Only different from the reference code I used, is that I do not use openssl_pkey_get_details($pubKey) because I will get below error in addition to the existing signature error:
OpenSSL error openssl_verify error:0906D06C:PEM routines:PEM_read_bio:no start line
OpenSSL error openssl_verify error:04091068:rsa routines:INT_RSA_VERIFY:bad signature
Also I've tried a variation by not using base64_decode() on the header but that would get the same return result (0) with error stating:
OpenSSL error openssl_verify error:04091077:rsa routines:INT_RSA_VERIFY:wrong signature length
What is wrong with the signature?
You may want to use this piece of code:
$pubKey = openssl_pkey_get_public(file_get_contents($headers['PAYPAL-CERT-URL']));
$details = openssl_pkey_get_details($pubKey);
$verifyResult = openssl_verify($sigString, base64_decode($headers['PAYPAL-TRANSMISSION-SIG']), $details['key'], 'sha256WithRSAEncryption');
if ($verifyResult === 0) {
throw new Exception('signature incorrect');
} elseif ($verifyResult === -1) {
throw new Exception('error checking signature');
}
The formula is <transmissionId>|<timeStamp>|<webhookId>|<crc32> not <transmissionId>|<timeStamp>|<eventId>|<crc32>. Also note that Webhook simulator events can't be verified.
This may not be exactly what you were looking for, but an alternative to manually validating the signature with Open SSL could be to use the PayPal PHP Restful API.
The PayPal Restful API exposes an endpoint that allows you to validate webhook: /v1/notifications/verify-webhook-signature
The PayPal-PHP-SDK provides a VerifyWebhookSignature class that make it easy to make calls to that end point.
They also have a Sample Script illustrating how to use VerifyWebhookSignature class.
As #JUBEI mentioned, you need to get the WEBHOOK_ID from your PayPal account and NOT from the headers you've received, remember the first time you've registered the webhook event, you must find your webhook ID right there.
Plus, make sure to use OPENSSL_ALGO_SHA256 instead of: 'sha256WithRSAEncryption', refer to: https://www.php.net/manual/en/openssl.signature-algos.php

JavaScript widget with trust based authentication under active directory

I'm building a new project and I'm having some debate over how it needs to be developed. The big picture is to develop a consumable JavaScript widget that other internal developers can embed into their web applications. The trick is that the consumer needs to be able to tell me what AD user is currently logged into their page...and then I need to trust that the passed username is coming from the consumer and hasn't been tampered with via outside sources.
The overall solution needs to have a VERY simple set-up on the consuming side involving no compiled code changes. Also it needs to be functional across both ASP.net and PHP applications (hence my decision to go with JavaScript).
Overall, it's kind of like an Oauth solution...except the trust between domains can be intrinsic since I'll already know every user in the company trusts the host domain.
I started stubbing it out and got kind of stuck. My idea was that I would basically host a JavaScript file that the client host could embed in their page. During their page load cycle, they could init my JavaScript widget and pass it a plain text username (all I really need). Somehow I would establish an secure trust between the client host's web page, and my widget so that it would be impossible for a third-party to embed my widget into a false web page and send action commands under a user other than their own.
I hope this makes sense to someone.
I haven't really discovered an answer so to speak, but I've decided on a method:
So, I decided on a pattern where I write my JavaScript and HTML widget using the proposed jQuery UI Widget Factory. That allows the my consumer to implement the widget using simple syntax like:
<script src="widget.js"></script>
$('#someElement').myWidget({ encryptionUrl: handlerPath });
Now, you'll noticed that as part of my widget, I ask the consumer to pass a "handlerPath." The "handler" is simply an Microsoft MVC Controller which is in charge of getting the logged in user, and encrypting the call.
So the handler in my app looks something like this...
[Authorize]
public JsonpResult GetToken(string body, string title, string sender)
{
Packet token = new Packet();
try
{
// Get the widget host's public cert
string publicKey = "some.ssl.key.name.here";
// Get the consumer host's private cert
string privateKey = "this.consumers.ssl.key.name.here";
// Build a simple message object containing secure details
// Specifically, the Body will have action items (in JSON) from my widget
// The User will be generated from the consumer's backend, thus secure
Message message = new Message(){
Body = body,
Title = title,
User = System.Web.HttpContext.Current.User.Identity.Name,
EncryptionServerIP = Request.UserHostAddress,
Sender = new Uri(sender),
EncryptionTime = DateTime.Now
};
PacketEncryption encryption = new PacketEncryption();
// This class just wraps basic encryption and signing methods
token = encryption.EncryptAndSign(message, publicKey, privateKey);
token.Trust = "thisConsumerTrustName";
}
catch (Exception exception)
{
throw;
}
return this.Jsonp(token);
}
Now, I have an encrypted "token" which has been encrypted using the widget host's public key, and signed using the widget consumer's private key. This "token" is passed back to the widget via JSONP from the consuming server.
My widget then sends this "token" (still as JSONP) to it's host server. The widget hosting server has decrypting logic which looks something like this.
public Message DecryptAndVerify(Packet packet, string requestIP)
{
if (packet == null) throw new ArgumentNullException("packet");
if (requestIP == null) throw new ArgumentNullException("requestIP");
Message message = new Message();
try
{
// Decrypt using the widget host's private key
RSAEncryption decrypto = new RSAEncryption("MyPrivateKey");
// Verify the signature using the "trust's" public key
// This is important because like you'll notice, I get the trust name
// from the encrypted packet. I then maintain a "trust store" mapping
// in my web.config, or SQL server
RSAEncryption verifyo = new RSAEncryption(GetPublicKeyFromTrust(packet.Trust));
string decryptedJson = decrypto.DecryptString(packet.EncryptedData);
// Verify the signature
if (!verifyo.Verify(decryptedJson, packet.Signature))
{
Exception ex = new Exception("Secure packet was not verified. Tamper evident");
throw ex;
}
// If the message is encrypted correctly, turn it into a message object
message = decryptedJson.FromJson<Message>();
// Verify the ip
if (message.EncryptionServerIP != requestIP)
{
Exception ex = new Exception("Request IP does not match encryption IP. Tamper evident");
throw ex;
}
// Verify the time
if ((DateTime.Now - message.EncryptionTime).Seconds > 30)
{
Exception ex = new Exception("Secure packet is too old");
throw ex;
}
}
catch (Exception ex)
{
throw ex;
}
return message;
}
The idea is that the JavaScript widget determines the secure actions the end user wants to take. Then it calls back to it's host (using the handler path provided by the consumer) and requests an encrypted token. That token contains the IP address of the caller, a timestamp, the current AD username, and a bundle of actions to be completed. Once the widget receives the token, it passes it over to it's own host server at which point the server checks to make sure that it is
Signed and encrypted properly according to predefined trusts
Not older than 30 seconds
From the same IP as the initial request to the consumer's server
After I determine those checks to be valid I can act on the user's actions by creating a WindowsPrincipal identity from the string username like this:
WindowsPrincipal pFoo = new WindowsPrincipal(new WindowsIdentity("username"));
bool test = pFoo.IsInRole("some role");
All said and done, I have established a trusted request from the widget consumer, and I no longer have to prompt for authentication.
Hopefully this helps you out. It's been running in my internal environment for about a month of QA and it's it's working great so far.

Categories