Stripe symfony subscription user - stripe-payments

I'm new to stripe, I managed to make the payment via the doc but I can't link my user to the subscription, for example I would like that when the payment is made, in my user entity I would like whether the user is put in premium or when the subscription is cancelled, whether the user is not in premium.
In my user entity I put a "premium" boolean field
Can you help me please?
#[Route('/abonnement/panier/2/checkout', name: 'aboCheck')]
public function stripe2(Request $request): Response
{
if (!$this->getUser()) {
return $this->redirectToRoute('public');
}
\Stripe\Stripe::setApiKey('sk_test....');
$priceId = 'price.....';
if (!$priceId) {
throw $this->createNotFoundException('Bad plan id!');
}
$username = $this->getUser()->getUsername();
$email = $this->getUser()->getEmail();
$session = \Stripe\Checkout\Session::create([
'success_url' => 'http://127.0.0.1:8000/abonnement/panier/2/order/success?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => 'http://127.0.0.1:8000/abonnement/panier/2',
'mode' => 'subscription',
'line_items' => [[
'price' => $priceId,
// For metered billing, do not pass quantity
'quantity' => 1,
]],
'client_reference_id' => $username,
'customer_email' => $email,
'metadata' => [
'username' => $username
]
]);
return $this->redirect($session->url);
}
#[Route('/webhook', name: 'webhook')]
public function handle(ManagerRegistry $doctrine)
{
\Stripe\Stripe::setApiKey('sk....');
$endpoint_secret = 'whsec.....';
$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();
}
$user = $this->getUser();
// Handle the event
switch ($event->type) {
case 'customer.subscription.trial_will_end':
$subscription = $event->data->object;
print_r($subscription); // contains a \Stripe\Subscription
// Then define and call a method to handle the trial ending.
// handleTrialWillEnd($subscription);
break;
case 'customer.subscription.created':
$subscription = $event->data->object;
print_r($subscription); // contains a \Stripe\Subscription
// Then define and call a method to handle the subscription being created.
// handleSubscriptionCreated($subscription);
break;
case 'customer.subscription.deleted':
$subscription = $event->data->object;
print_r($subscription); // contains a \Stripe\Subscription
// Then define and call a method to handle the subscription being deleted.
// handleSubscriptionDeleted($subscription);
break;
case 'customer.subscription.updated':
$subscription = $event->data->object;
print_r($subscription); // contains a \Stripe\Subscription
// Then define and call a method to handle the subscription being updated.
// handleSubscriptionUpdated($subscription);
break;
default:
// Unexpected event type
error_log('Received unknown event type');
}
return new Response(Response::HTTP_OK);
}
#[Route('/abonnement/panier/2/order/success', name: 'success')]
public function successStripe(ManagerRegistry $doctrine, Request $request): Response
{
if (!$this->getUser()) {
return $this->redirectToRoute('public');
}
$priceId = 'price......';
\Stripe\Stripe::setApiKey('sk......');
// Set your secret key. Remember to switch to your live secret key in production.
// See your keys here: https://dashboard.stripe.com/apikeys
$myuser = $this->getUser();
$session = \Stripe\Checkout\Session::retrieve($request->get('session_id'));
$customer = \Stripe\Customer::retrieve($session->customer);
if (($session->status == 'complete') & ($customer->email == $myuser->getEmail())) {
$entityManager = $doctrine->getManager();
$myuser->setProfileId($customer->id);
$myuser->setPremium(true);
$entityManager->flush();
} else {
return $this->redirectToRoute('aboStripe');
}
return $this->render('stripe/success.html.twig', [
'customer' => $customer
]);
}
#[Route('/customer-portal', name: 'customer-portal')]
public function portal(): Response
{
// Set your secret key. Remember to switch to your live secret key in production.
// See your keys here: https://dashboard.stripe.com/apikeys
\Stripe\Stripe::setApiKey('sk_test.....');
// This is the URL to which the user will be redirected after they have
// finished managing their billing in the portal.
$myuser = $this->getUser();
$stripe_customer_id = $myuser->getProfileId();
$session = \Stripe\BillingPortal\Session::create([
'customer' => $stripe_customer_id,
'return_url' => 'http://127.0.0.1:8000/abonnement/panier/2',
]);
return $this->redirect($session->url);
}

Related

Creating and Capturing payment with paypal v2 node.js

I am trying to integrate PayPal server-side payment on a website with a new v2 since v1 is deprecated. Since I had to include quite a lot of code from their git, i will post here everything for a working example
In v1 i could do it really easily:
app.get('/create', function (req, res) {
//build PayPal payment request
let payReq = JSON.stringify({
'intent': 'sale',
'redirect_urls': {
'return_url': 'http://localhost:8081/process',
'cancel_url': 'http://localhost:8081/cancel'
},
'payer': {
'payment_method': 'paypal'
},
'transactions': [{
'amount': {
'total': '7.47',
'currency': 'USD'
},
'description': 'This is the payment transaction description.'
}]
});
paypal.payment.create(payReq, function (error, payment) {
if (error) {
console.error(error);
} else {
//capture HATEOAS links
let links = {};
payment.links.forEach(function (linkObj) {
links[linkObj.rel] = {
'href': linkObj.href,
'method': linkObj.method
};
})
//if redirect url present, redirect user
if (links.hasOwnProperty('approval_url')) {
res.redirect(links['approval_url'].href);
} else {
console.error('no redirect URI present');
}
}
});
});
app.get('/process', function (req, res) {
let paymentId = req.query.paymentId;
let payerId = {'payer_id': req.query.PayerID};
paypal.payment.execute(paymentId, payerId, function (error, payment) {
if (error) {
console.error(error);
} else {
if (payment.state == 'approved') {
const payerCountry = payment.payer.payer_info.country_code;
const total = payment.transactions[0].amount.total;
const currency = payment.transactions[0].amount.currency;
savePayment(payerCountry, total, currency);
res.send('payment completed successfully ' + cnt++);
} else {
res.send('payment not successful');
}
}
});
});
The create endpoint basically creates the order, the paypal API returns hateos links and the controller says the browser should redirect to that link. Once redirected , if user approves payment on paypal site he is redirected to on of
'redirect_urls': {
'return_url': 'http://localhost:8081/process',
'cancel_url': 'http://localhost:8081/cancel'
},
process endpoints retrieves PAYMENT ID and PAYER ID from query and captures the order - allowing me to do whatever i want to do ( e.g save payment in db ) in callback.
Now v2 seems like huge mess:
Following devs guide i have created
Paypal button (copy paste ):
import React, {useEffect} from "react";
import {PayPalButtons, PayPalScriptProvider, usePayPalScriptReducer} from "#paypal/react-paypal-js";
// This values are the props in the UI
const amount = "2";
const currency = "USD";
const style = {"layout": "vertical"};
const ButtonWrapper = ({currency, showSpinner}) => {
// usePayPalScriptReducer can be use only inside children of PayPalScriptProviders
// This is the main reason to wrap the PayPalButtons in a new component
const [{options, isPending}, dispatch] = usePayPalScriptReducer();
useEffect(() => {
dispatch({
type: "resetOptions",
value: {
...options,
currency: currency,
},
});
}, [currency, showSpinner]);
const createOrder = (data, actions) => {
console.log("create")
return fetch('http://localhost:8081/create', {
method: 'post'
}).then(function (res) {
return res.json();
}).then(function (orderData) {
console.log(orderData);
window.location = orderData.redirect;
});
}
// Call your server to finalize the transaction
const onApprove = (data, actions) => {
console.log("approve")
return fetch('/process', {
method: 'post'
}).then(function (res) {
return res.json();
}).then(function (orderData) {
// Three cases to handle:
// (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
// (2) Other non-recoverable errors -> Show a failure message
// (3) Successful transaction -> Show confirmation or thank you
// This example reads a v2/checkout/orders capture response, propagated from the server
// You could use a different API or structure for your 'orderData'
var errorDetail = Array.isArray(orderData.details) && orderData.details[0];
if (errorDetail && errorDetail.issue === 'INSTRUMENT_DECLINED') {
return actions.restart(); // Recoverable state, per:
// https://developer.paypal.com/docs/checkout/integration-features/funding-failure/
}
if (errorDetail) {
var msg = 'Sorry, your transaction could not be processed.';
if (errorDetail.description) msg += '\n\n' + errorDetail.description;
if (orderData.debug_id) msg += ' (' + orderData.debug_id + ')';
return alert(msg); // Show a failure message (try to avoid alerts in production environments)
}
// Successful capture! For demo purposes:
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
var transaction = orderData.purchase_units[0].payments.captures[0];
alert('Transaction ' + transaction.status + ': ' + transaction.id + '\n\nSee console for all available details');
});
}
return (<>
{(showSpinner && isPending) && <div className="spinner"/>}
<PayPalButtons
style={style}
disabled={false}
forceReRender={[amount, currency, style]}
fundingSource={undefined}
createOrder={(data, actions) => createOrder(data, actions)}
onApprove={(data, actions) => onApprove(data, actions)}
/>
</>
);
}
export default function PayPalButton() {
return (
<div style={{ maxWidth: "750px", minHeight: "200px" }}>
<PayPalScriptProvider
options={{
"client-id": "test",
components: "buttons",
currency: "USD"
}}
>
<ButtonWrapper
currency={currency}
showSpinner={false}
/>
</PayPalScriptProvider>
</div>
);
}
And then followed the flow from paypal's github example
created their HttpClient
const checkoutNodeJssdk = require('#paypal/checkout-server-sdk');
/**
* Returns PayPal HTTP client instance with environment which has access
* credentials context. This can be used invoke PayPal API's provided the
* credentials have the access to do so.
*/
function client() {
return new checkoutNodeJssdk.core.PayPalHttpClient(environment());
}
/**
* Setting up and Returns PayPal SDK environment with PayPal Access credentials.
* For demo purpose, we are using SandboxEnvironment. In production this will be
* LiveEnvironment.
*/
function environment() {
let clientId = process.env.PAYPAL_CLIENT_ID || '<clientId>';
let clientSecret = process.env.PAYPAL_CLIENT_SECRET || '<secret>';
if (process.env.NODE_ENV === 'production') {
return new checkoutNodeJssdk.core.LiveEnvironment(clientId, clientSecret);
}
return new checkoutNodeJssdk.core.SandboxEnvironment(clientId, clientSecret);
}
async function prettyPrint(jsonData, pre=""){
let pretty = "";
function capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
}
for (let key in jsonData){
if (jsonData.hasOwnProperty(key)){
if (isNaN(key))
pretty += pre + capitalize(key) + ": ";
else
pretty += pre + (parseInt(key) + 1) + ": ";
if (typeof jsonData[key] === "object"){
pretty += "\n";
pretty += await prettyPrint(jsonData[key], pre + "\t");
}
else {
pretty += jsonData[key] + "\n";
}
}
}
return pretty;
}
module.exports = {client: client, prettyPrint:prettyPrint};
created their CreateOrder:
/**
* PayPal SDK dependency
*/
const checkoutNodeJssdk = require('#paypal/checkout-server-sdk');
/**
* PayPal HTTP client dependency
*/
const payPalClient = require('./PayPalClient');
/**
* Setting up the JSON request body for creating the Order. The Intent in the
* request body should be set as "CAPTURE" for capture intent flow.
*
*/
function buildRequestBody() {
return {
"intent": "CAPTURE",
"application_context": {
"return_url": "http://localhost:8081/process",
"cancel_url": "https://www.example.com",
"locale": "en-US",
"landing_page": "BILLING",
"user_action": "CONTINUE"
},
"purchase_units": [
{
"soft_descriptor": "Donation",
"amount": {
"currency_code": "USD",
"value": "220.00"
}
}
]
};
}
/**
* This is the sample function which can be sued to create an order. It uses the
* JSON body returned by buildRequestBody() to create an new Order.
*/
async function createOrder(debug=false) {
try {
const request = new checkoutNodeJssdk.orders.OrdersCreateRequest();
request.headers["prefer"] = "return=representation";
request.requestBody(buildRequestBody());
const response = await payPalClient.client().execute(request);
if (debug){
console.log("Status Code: " + response.statusCode);
console.log("Status: " + response.result.status);
console.log("Order ID: " + response.result.id);
console.log("Intent: " + response.result.intent);
console.log("Links: ");
response.result.links.forEach((item, index) => {
let rel = item.rel;
let href = item.href;
let method = item.method;
let message = `\t${rel}: ${href}\tCall Type: ${method}`;
console.log(message);
});
console.log(`Gross Amount: ${response.result.purchase_units[0].amount.currency_code} ${response.result.purchase_units[0].amount.value}`);
// To toggle print the whole body comment/uncomment the below line
console.log(JSON.stringify(response.result, null, 4));
}
return response;
}
catch (e) {
console.log(e)
}
}
/**
* This is the driver function which invokes the createOrder function to create
* an sample order.
*/
if (require.main === module){
(async() => await createOrder(true))();
}
/**
* Exports the Create Order function. If needed this can be invoked from the
* order modules to execute the end to flow like create order, retrieve, capture
* and refund(Optional)
*/
module.exports = {createOrder:createOrder};
And endpoints:
const createUsersOrder = async (res) => {
try {
let response = await createOrder();
console.log("Creating Order...");
let orderId = "";
if (response.statusCode === 201){
console.log("Created Successfully");
orderId = response.result.id;
console.log("Links:");
response.result.links.forEach((item, index) => {
let rel = item.rel;
let href = item.href;
let method = item.method;
let message = `\t${rel}: ${href}\tCall Type: ${method}`;
console.log(message);
});
let links = {};
response.result.links.forEach(function (linkObj) {
links[linkObj.rel] = {
'href': linkObj.href,
'method': linkObj.method
};
})
//if redirect url present, redirect user
if (links.hasOwnProperty('approve')) {
var returnObj = {redirect : links['approve'].href}
console.log("Returning " + returnObj)
res.send(returnObj);
} else {
console.error('no redirect URI present');
}
}
console.log("Copy approve link and paste it in browser. Login with buyer account and follow the instructions.\nOnce approved hit enter...");
return
} catch (error) {
console.log('There was an error: ', error);
}
};
app.post("/create", function(req,res) {
createUsersOrder(res);
})
Here, this is called when button is clicked, as "createOrder" method is called. I create order just like in the v1 code, and then redirect browser to the url. However when the user approves transaction on paypal, and thus is being redirected to one of
"application_context": {
"return_url": "http://localhost:8081/process",
"cancel_url": "http://localhost:8081/cancel",
"locale": "en-US",
"landing_page": "BILLING",
"user_action": "CONTINUE"
},
return url ( /process route for success ), the request DOES NOT contain payment_id, only PAYER_ID. But the payment_id ( or order_id in v2 ) is needed to capture the order.
Since i have found literally zero blogs, tutorials, guide for v2 ( only millions for v1) i am confused where to get the order id. Do i really need to save it in DB after i create the order? OR is there any other trick?
Also, the button contains onApprove method, but after creating order, the browser is redirected to paypal, and the paypal redirects user to http://localhost:8081/process endpoint - thus the onApprove method is never invoked and useless (?).
This whole flow of v2 is really confusing, is there something i am missing?
Thanks for help
With your v2 code, do not use any redirects. At all. Your button should call 2 endpoints on your server. These two endpoints should (respectively) do the API operations of creating and capturing the order, and return the JSON result in each case (the capture route can do any server-side operations like storing the transaction result in the database before forwarding the JSON result to the client caller, since the client needs to handle any capture error situations as well as showing a success message). You can find a full stack node.js example in the PayPal integration guide, but it's fine to keep your #paypal/react-paypal-js code pretty much as-is for the front end.

how to get transaction id in stripe payment gateway

I am using stripe payment gateway in php. Everything is working. But not showing transaction id after successful payment. How to get transaction id?
if(isset($_POST['stripeToken']))
{
$amount_cents = str_replace(".","","1.00"); // Chargeble amount
$invoiceid = "14526321"; // Invoice ID
$description = "Invoice #" . $invoiceid . " - " . $invoiceid;
try {
$charge = Stripe_Charge::create(array(
"amount" => $amount_cents,
"currency" => "usd",
"source" => $_POST['stripeToken'],
"description" => $description)
);
// Payment has succeeded, no exceptions were thrown or otherwise caught
$result = "success";
} catch(Stripe_CardError $e) {
$error = $e->getMessage();
$result = "declined";
} catch (Stripe_InvalidRequestError $e) {
$result = "declined";
} catch (Stripe_AuthenticationError $e) {
$result = "declined";
} catch (Stripe_ApiConnectionError $e) {
$result = "declined";
} catch (Stripe_Error $e) {
$result = "declined";
} catch (Exception $e) {
}
// Charge the Customer instead of the card
if($result=="success") {
$response = "<div class='col-sm-offset-3 col-sm-9 text-success'>Your Payment has been processed successfully.</div>";
} else{
$response = "<div class='text-danger'>Stripe Payment Status : \".$result.</div>";
}
}
?>
Above code is for after form submit.
You are getting your API response in the variable $charge. Comment out $result = s"success"; and write echo($charge);. You will see the response. In the response, the 'balance_transaction' value that starts with 'tx_' is the transaction id. Do save it to your database for further use.
Check the stripe API docs: https://stripe.com/docs/api/charges/object

Is there a way to proactively end a prompt, i.e with a timer or event, from the WaterfallStepContext in the Microsoft Bot Framework

From what I see, in the nodejs framework, I notice you can't prompt a user within a dialogflow dialog and retrieve the that prompt within the same dialog function... I may be wrong about that. Either way, you can return prompt and retrieve it in the next dialog flow.
Is there a way to cut off the awaiting prompt for user input and end or resume an activity of the dialog?
I would like to cut the prompt off at a certain point, ideally on a time basis.
This is possible thru using a combination of sending a proactive message, filtering via onMessage() and onSendActivities, and a dialog interruption.
First, in index.js, I setup an API for sending proactive messages.
server.get('/api/interrupt', async (req, res) => {
for (const conversationReference of Object.values(conversationReferences)) {
await adapter.continueConversation(conversationReference, async turnContext => {
var reply = { type: ActivityTypes.Message };
const yesBtn = { type: ActionTypes.PostBack, title: 'Yes', value: 'Yes' };
const noBtn = { type: ActionTypes.PostBack, title: 'No', value: 'No' };
const card = CardFactory.heroCard(
'Want to skip out and see Star Wars?',
null,
[ yesBtn, noBtn ]
);
reply.attachments = [ card ];
await turnContext.sendActivity(reply);
return { status: DialogTurnStatus.waiting };
});
}
res.setHeader('Content-Type', 'text/html');
res.writeHead(200);
res.write('<html><body><h1>Proactive messages have been sent.</h1></body></html>');
res.end();
});
In mainBot.js:
I create a state property for setting whether the timer should be active or not. I also create an empty timer object.
I assign the timer state property to a variable (activityTimer) along with a default value (null).
In onMessage(), I filter on channelId ("directline") and role ("user").
I filter on whether the user selected the 'Yes' option from the proactive message. If so, the dialog(s) are cancelled, timerState is set to false, and the timer is cleared. This ensures the timer won't fire once the dialog is cancelled. New input from the user is required to start the dialog, again.
Any other user input sets the timerState to true and clears the timer. This ensures multiple timers do not stack when new user input is received by clearing any current timers and flagging a new timer to start.
Lastly, if timerState is set to true, then setTimeout() is called, assigned to the timer variable, with a timeout value of 5 seconds. At this point, an API call is made to the "interrupt" endpoint I created in index.js, which sends the proactive message, and timerState is set back to false.
this.activityTimerAccessor = conversationState.createProperty('activityTimer');
let timer;
this.onMessage(async (context, next) => {
if (activity.channelId === 'directline') {
if (activity.from.role === 'user') {
let text = activity.text.toLowerCase();
switch (text === 'yes') {
case true:
if (activity.channelData.postBack) {
activityTimer.timerState = false;
}
if (timer) clearTimeout(timer);
break;
default:
activityTimer.timerState = true;
if (timer) clearTimeout(timer);
}
}
if (activityTimer.timerState === true) {
timer = await setTimeout(async () => {
await fetch('http://localhost:3978/api/interrupt');
}, 5000);
activityTimer.timerState = false;
}
}
}
In mainDialog.js, I extend cancelAndHelpDialog.js which is where any interruptions are caught. If you are unfamiliar with interruptions, you can read about them in the docs here and reference a sample here.
const { CancelAndHelpDialog } = require( './cancelAndHelpDialog' );
const MAIN_WATERFALL_DIALOG = 'MainWaterfallDialog';
class MainDialog extends CancelAndHelpDialog {
constructor ( id, userState, conversationState ) {
super( id, userState, conversationState );
this.mainId = id;
this.userState = userState;
this.conversationState = conversationState;
this.addDialog( new AdaptiveCardDialog( ADAPTIVE_CARD_WATERFALL_DIALOG ) )
.addDialog( new WaterfallDialog( MAIN_WATERFALL_DIALOG, [
this.simpleSend.bind(this)
] ) ) ;
this.initialDialogId = MAIN_WATERFALL_DIALOG;
}
async simpleSend ( stepContext ) {
await stepContext.context.sendActivity('Looks like you said or did something...')
return await stepContext.next();
}
}
module.exports.MainDialog = MainDialog;
In cancelAndHelpDialog.js, I create a new ComponentDialog which contains an "interrupt" method. In interrupt(), I filter for the user selecting the "Yes" button from the proactive message indicating the user's wish to cancel the dialog(s). If pressed, I notify the user and cancel all dialogs.
class CancelAndHelpDialog extends ComponentDialog {
[...]
async interrupt(innerDc, next) {
const activity = innerDc.context.activity;
const text = innerDc.context.activity.text.toLowerCase();
if (activity.type === 'message' && activity.channelId === 'directline') {
if (text === 'yes' && activity.channelData.postBack === true) {
await innerDc.context.sendActivity("Cancelling...Let's go see a movie!!");
return await innerDc.cancelAllDialogs();
}
}
}
}
This should set you up going in the right direction. Obviously, alter to fit your needs. For instance, the proactive message doesn't have to take user input sending just a message that is picked up and triggers an interruption.
Hope of help!

How to configure docusign demo to live after promoting integration key

I used embedded signing and after successfully running on demo, promoted integration key and passed review.
What changes I need to in my code? Where I need to put promoted integration key? I know live have no token key integration like demo which is for 8 hours only.
I changed https://demo.docusign.net/restapi to https://www.docusign.net/restapi
$dsConfig = array(
'ds_client_id' => isset($session->data['user_id']) ? $session->data['user_id'] : "1",
'ds_client_secret' => 'eyJ0eXAiOiJNVCIsImFsZxxxxxxxxxxxxxx',
'signer_email' => $customer->isLogged() ? $customer->getEmail() : 'user#example.com',
'signer_name' => $customer->isLogged() ? $customer->getFirstName() . ' ' . $customer->getLastName() : 'John Doe',
'app_url' => HTTPS_SERVER . 'public/agreement-accept.html',
'authorization_server' => 'https://demo.docusign.net/restapi',
'session_secret' => isset($session->data['token']) ? $session->data['token'] : md5(time()),
'allow_silent_authentication' => true,
'accountId' => 'xxxxxxx',
'templateId' => 'xxxxx-xxxx-xxx-xxx-xxxx'
);
$config = new Configuration();
$config->setHost($dsConfig['authorization_server']);
$config->addDefaultHeader(
"Authorization",
"Bearer " . $dsConfig['ds_client_secret']
);
$apiClient = new ApiClient($config);
https://docusign.net/restapi is not a valid API endpoint.
In order to make API calls using your integration key, you'll need to implement one of the Authentication Workflows as documented here: https://developers.docusign.com/esign-rest-api/guides/authentication/
Once you have a valid token, you'll need to make a User Info call to determine the base URI for your account: https://developers.docusign.com/esign-rest-api/guides/authentication/user-info-endpoints
I did it by creating a generateRefreshToken() function in ApiClient Class:-
public function generateRefreshToken($client_id = null, $client_secret = null, $refresh_token = null)
{
if (!$client_id) {
throw new \InvalidArgumentException('Missing the required parameter $client_id when calling generateAccessToken');
}
if (!$client_secret || !$refresh_token) {
throw new \InvalidArgumentException('Missing the required parameter $client_secret when calling generateAccessToken');
}
if (!$refresh_token) {
throw new \InvalidArgumentException('Missing the required parameter $refresh_token when calling generateAccessToken');
}
$resourcePath = "/oauth/token";
$queryParams = array();
$integrator_and_secret_key = "Basic " . utf8_decode(base64_encode("{$client_id}:{$client_secret}"));
$headers = array(
"Authorization" => $integrator_and_secret_key,
"Content-Type" => "application/x-www-form-urlencoded",
);
$postData = array(
"grant_type" => "refresh_token",
"refresh_token" => $refresh_token
);
list($response, $statusCode, $httpHeader) = $this->callApi($resourcePath, self::$POST, $queryParams, $postData, $headers, null, null, true);
if(isset($response->access_token))
$this->config->addDefaultHeader("Authorization", "{$response->token_type} {$response->access_token}");
return array($this->getSerializer()->deserialize($response, '\DocuSign\eSign\Client\Auth\OAuthToken', $httpHeader), $statusCode, $httpHeader);
}
protected static function requestNewToken($refresh_token) {
$config = new Configuration();
$apiClient = new ApiClient($config);
$token = $apiClient->generateRefreshToken('clientid', 'secret_key', $refresh_token);
return array($token[0]['access_token'],$token[0]['refresh_token'],time() + 86400 * 27);
}
protected static function getToken() {
$tokenFile = __DIR__ . '/token.json';
$access_token = null;
if (file_exists($tokenFile)) {
$data = json_decode(file_get_contents($tokenFile), true);
$access_token = $data['access_token']; // todo consider field names
$refresh_token = $data['refresh_token']; // todo consider field names
$expires_in = $data['expires_in']; // todo consider field names
if ($expires_in <= time()) {
$access_token = null;
}
}
if (!$access_token) {
list($access_token,$refresh_newtoken,$expires_in) = self::requestNewToken($refresh_token);
if (!$access_token || !$refresh_newtoken || !$expires_in) {
throw new Exception('Could not request new token.');
}
file_put_contents($tokenFile, json_encode(array(
'access_token' => $access_token,
'refresh_token' => $refresh_newtoken,
'expires_in' => $expires_in
)));
}
return $access_token;
}
Also https://www.docusign.net/restapi is a valid API endpoint for production.It works for me. https:// and www should be there...
$dsConfig = array(
'ds_client_id' => isset($session->data['user_id']) ? $session->data['user_id'] : "1",
// The app's DocuSign integration key's secret
'ds_client_secret' => $token,
'signer_email' => $customer->isLogged() ? $customer->getEmail() : 'user#example.com',
'signer_name' => $customer->isLogged() ? $customer->getFirstName() . ' ' . $customer->getLastName() : 'John Doe',
// return url
'app_url' => HTTPS_SERVER . 'public/agreement-accept.html',
'authorization_server' => 'https://www.docusign.net/restapi',
'session_secret' => isset($session->data['token']) ? $session->data['token'] : md5(time()),
'allow_silent_authentication' => true,
'accountId' => 'xxx',
'templateId' => 'xxxx'
);

Stripe with multi step form user registration

So I started playing with stripe yesterday and so far so good. I have a three step registration form, first is personal info (name), second one is password and email and third tab is for payment (monthly subscription). My logic is that if payment succeeded, then create the account on my end, if it did not, then throw the error.
So far so good- my logic works, but I have few issues. As I read this documentation then I should store the customer ID in database for later use and checking if the subscription is active or not. In my function stripeResponseHandler(status, response) {} when I console.log(response) I see the token code and card information, but I never see the customer object.
Here comes few questions:
Is my method and logic safe what I described in the first paragraph?
How can I get the customer object so I could insert the id and corresponding data into the database during the registration process?
JS
function stripeResponseHandler(status, response) {
// Grab the form:
var $form = $("#registrationForm"),
formData = new FormData(),
params = $form.serializeArray();
if (response.error) { // Problem!
// Show the errors on the form:
console.log(response.error.message);
$form.find('#btn-signup').prop('disabled', false); // Re-enable submission
} else { // Token was created!
// Get the token ID:
var token = response.id;
console.log(token);
console.log(response);
// Insert the token ID into the form so it gets submitted to the server:
$form.append($('<input type="hidden" name="stripeToken">').val(token));
// Submit the form:
$.ajax({
url: $form.attr('action'),
type: 'POST',
data: $form.serialize(),
cache: false,
success: function(data) {
$("#formAlert").empty();
console.log(data);
var items = '';
$.each(data.status, function(key, val) {
items += '<li>' + val + '</li>';
$(".se-pre-con").fadeOut("slow");
});
$("#registerAlerts").append(items);
},
error: function(jqXHR, textStatus, errorThrown, data) {
$(".se-pre-con").fadeOut("slow");
console.log(jqXHR, textStatus, errorThrown, data);
}
});
}
}
$("#btn-signup").on("click", function(e) {
e.preventDefault();
$(".se-pre-con").show();
var $form = $("#registrationForm");
// Request a token from Stripe:
Stripe.card.createToken({
number: $('.card-number').val(),
cvc: $('.card-cvc').val(),
exp_month: $('.card-expiry-month').val(),
exp_year: $('.card-expiry-year').val()
}, stripeResponseHandler);
});
PHP
<?php
header('Content-type: application/json');
require_once 'class.user.php';
require_once ('../plugins/stripe/init.php');
$errors = 0;
if ($stmt->rowCount() > 0)
{
$errors++;
$errors_query["status"][] = 'User exists';
}
if ($errors == 0)
{
try
{
$customer = StripeCustomer::create(array(
'email' => $_POST['formEmail'],
'source' => $token,
'plan' => '123456789',
"description" => "Subscription for user " . $uFirstname
));
if ($reg_user->register($email, $upass, $code, $utype, $uFirstname, $uLastname, $termAndAgreement))
{
$user_id = $reg_user->lasdID();
$family_files = $reg_user->runQuery("INSERT into user_files (user_id, file_type, file_size, file_name, file_new_name, file_path, file_cat, file_folder, date_created) VALUES (:user_id, 'X', '0', 'X', 'X', 'X', 'profilePicture', 'profilePicture', NOW())");
$family_files->bindparam(':user_id', $user_id, PDO::PARAM_STR);
$family_files->execute();
}
else
{
$errors++;
$errors_query["status"][] = 'error';
}
}
catch(Exception $e)
{
$errors++;
$errors_query["status"][] = 'Error with payment';
$e->getMessage();
}
}
if ($errors > 0)
{
echo json_encode($errors_query);
exit();
}
else
{
$errors_query["status"][] = 'user_welcome';
echo json_encode($errors_query);
exit();
}
?>
Using custom Stripe Payment form is completely safe. Stripe also wrote in their documentation about it and as long as you are using Stripe.js it is safe.
When you register any user using the credit card then it always returns a Stripe token. It will not return the customer id nither customer is created at this time so for this you have to call the create customer API and pass the above token in it and It will return customer object using customer Id.
you can read about stripe create customer API here

Resources