How to render Google Pay button in Google Apps Script - javascript

I want to install a Google Pay button into my Google Apps Script and have it work similar to this demo.
Here is the documentation.
But when I use the following code, I don't see any payment button.
What am I doing wrong?
Index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script async
  src="https://pay.google.com/gp/p/js/pay.js"
  onload="onGooglePayLoaded()">
</script>
</head>
<body>
<div id="container"></div>
</body>
</html>
Code.gs
// https://developers.google.com/apps-script/guides/html#code.gs_1
// Use this code for Google Docs, Slides, Forms, or Sheets.
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
.createMenu('Buy report')
.addItem('Open', 'openDialog')
.addToUi();
}
function openDialog() {
const html = HtmlService.createHtmlOutputFromFile( 'Index' );
SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
.showModalDialog( html, 'Dialog title' );
// .showSidebar( html, );
}
// ref: https://developers.google.com/pay/api/web/guides/resources/demos
// demo: https://jsfiddle.net/fw5t6caL/
/**
* Define the version of the Google Pay API referenced when creating your
* configuration
*
* #see {#link https://developers.google.com/pay/api/web/reference/request-objects#PaymentDataRequest|apiVersion in PaymentDataRequest}
*/
const baseRequest = {
apiVersion: 2,
apiVersionMinor: 0
};
/**
* Card networks supported by your site and your gateway
*
* #see {#link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
* #todo confirm card networks supported by your site and gateway
*/
const allowedCardNetworks = ["AMEX", "DISCOVER", "INTERAC", "JCB", "MASTERCARD", "VISA"];
/**
* Card authentication methods supported by your site and your gateway
*
* #see {#link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
* #todo confirm your processor supports Android device tokens for your
* supported card networks
*/
const allowedCardAuthMethods = ["PAN_ONLY", "CRYPTOGRAM_3DS"];
/**
* Identify your gateway and your site's gateway merchant identifier
*
* The Google Pay API response will return an encrypted payment method capable
* of being charged by a supported gateway after payer authorization
*
* #todo check with your gateway on the parameters to pass
* #see {#link https://developers.google.com/pay/api/web/reference/request-objects#gateway|PaymentMethodTokenizationSpecification}
*/
const tokenizationSpecification = {
type: 'PAYMENT_GATEWAY',
parameters: {
'gateway': 'example',
'gatewayMerchantId': 'exampleGatewayMerchantId'
}
};
/**
* Describe your site's support for the CARD payment method and its required
* fields
*
* #see {#link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
*/
const baseCardPaymentMethod = {
type: 'CARD',
parameters: {
allowedAuthMethods: allowedCardAuthMethods,
allowedCardNetworks: allowedCardNetworks
}
};
/**
* Describe your site's support for the CARD payment method including optional
* fields
*
* #see {#link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
*/
const cardPaymentMethod = Object.assign(
{},
baseCardPaymentMethod,
{
tokenizationSpecification: tokenizationSpecification
}
);
/**
* An initialized google.payments.api.PaymentsClient object or null if not yet set
*
* #see {#link getGooglePaymentsClient}
*/
let paymentsClient = null;
/**
* Configure your site's support for payment methods supported by the Google Pay
* API.
*
* Each member of allowedPaymentMethods should contain only the required fields,
* allowing reuse of this base request when determining a viewer's ability
* to pay and later requesting a supported payment method
*
* #returns {object} Google Pay API version, payment methods supported by the site
*/
function getGoogleIsReadyToPayRequest() {
return Object.assign(
{},
baseRequest,
{
allowedPaymentMethods: [baseCardPaymentMethod]
}
);
}
/**
* Configure support for the Google Pay API
*
* #see {#link https://developers.google.com/pay/api/web/reference/request-objects#PaymentDataRequest|PaymentDataRequest}
* #returns {object} PaymentDataRequest fields
*/
function getGooglePaymentDataRequest() {
const paymentDataRequest = Object.assign({}, baseRequest);
paymentDataRequest.allowedPaymentMethods = [cardPaymentMethod];
paymentDataRequest.transactionInfo = getGoogleTransactionInfo();
paymentDataRequest.merchantInfo = {
// #todo a merchant ID is available for a production environment after approval by Google
// See {#link https://developers.google.com/pay/api/web/guides/test-and-deploy/integration-checklist|Integration checklist}
// merchantId: '01234567890123456789',
merchantName: 'Example Merchant'
};
return paymentDataRequest;
}
/**
* Return an active PaymentsClient or initialize
*
* #see {#link https://developers.google.com/pay/api/web/reference/client#PaymentsClient|PaymentsClient constructor}
* #returns {google.payments.api.PaymentsClient} Google Pay API client
*/
function getGooglePaymentsClient() {
if ( paymentsClient === null ) {
paymentsClient = new google.payments.api.PaymentsClient({environment: 'TEST'});
}
return paymentsClient;
}
/**
* Initialize Google PaymentsClient after Google-hosted JavaScript has loaded
*
* Display a Google Pay payment button after confirmation of the viewer's
* ability to pay.
*/
function onGooglePayLoaded() {
const paymentsClient = getGooglePaymentsClient();
paymentsClient.isReadyToPay(getGoogleIsReadyToPayRequest())
.then(function(response) {
if (response.result) {
addGooglePayButton();
// #todo prefetch payment data to improve performance after confirming site functionality
// prefetchGooglePaymentData();
}
})
.catch(function(err) {
// show error in developer console for debugging
console.error(err);
});
}
/**
* Add a Google Pay purchase button alongside an existing checkout button
*
* #see {#link https://developers.google.com/pay/api/web/reference/request-objects#ButtonOptions|Button options}
* #see {#link https://developers.google.com/pay/api/web/guides/brand-guidelines|Google Pay brand guidelines}
*/
function addGooglePayButton() {
const paymentsClient = getGooglePaymentsClient();
const button =
paymentsClient.createButton({onClick: onGooglePaymentButtonClicked});
document.getElementById('container').appendChild(button);
}
/**
* Provide Google Pay API with a payment amount, currency, and amount status
*
* #see {#link https://developers.google.com/pay/api/web/reference/request-objects#TransactionInfo|TransactionInfo}
* #returns {object} transaction info, suitable for use as transactionInfo property of PaymentDataRequest
*/
function getGoogleTransactionInfo() {
return {
countryCode: 'US',
currencyCode: 'USD',
totalPriceStatus: 'FINAL',
// set to cart total
totalPrice: '1.00'
};
}
/**
* Prefetch payment data to improve performance
*
* #see {#link https://developers.google.com/pay/api/web/reference/client#prefetchPaymentData|prefetchPaymentData()}
*/
function prefetchGooglePaymentData() {
const paymentDataRequest = getGooglePaymentDataRequest();
// transactionInfo must be set but does not affect cache
paymentDataRequest.transactionInfo = {
totalPriceStatus: 'NOT_CURRENTLY_KNOWN',
currencyCode: 'USD'
};
const paymentsClient = getGooglePaymentsClient();
paymentsClient.prefetchPaymentData(paymentDataRequest);
}
/**
* Show Google Pay payment sheet when Google Pay payment button is clicked
*/
function onGooglePaymentButtonClicked() {
const paymentDataRequest = getGooglePaymentDataRequest();
paymentDataRequest.transactionInfo = getGoogleTransactionInfo();
const paymentsClient = getGooglePaymentsClient();
paymentsClient.loadPaymentData(paymentDataRequest)
.then(function(paymentData) {
// handle the response
processPayment(paymentData);
})
.catch(function(err) {
// show error in developer console for debugging
console.error(err);
});
}
/**
* Process payment data returned by the Google Pay API
*
* #param {object} paymentData response from Google Pay API after user approves payment
* #see {#link https://developers.google.com/pay/api/web/reference/response-objects#PaymentData|PaymentData object reference}
*/
function processPayment(paymentData) {
// show returned data in developer console for debugging
console.log(paymentData);
// #todo pass payment token to your gateway to process payment
paymentToken = paymentData.paymentMethodData.tokenizationData.token;
}

I think that when your script is the actual tested script, the Javascript is required to be included in the HTML side. So how about the following modification?
Modified script:
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script async
src="https://pay.google.com/gp/p/js/pay.js"
onload="onGooglePayLoaded()">
</script>
</head>
<body>
<div id="container"></div>
</body>
<script>
// ref: https://developers.google.com/pay/api/web/guides/resources/demos
// demo: https://jsfiddle.net/fw5t6caL/
/**
* Define the version of the Google Pay API referenced when creating your
* configuration
*
* #see {#link https://developers.google.com/pay/api/web/reference/request-objects#PaymentDataRequest|apiVersion in PaymentDataRequest}
*/
const baseRequest = {
apiVersion: 2,
apiVersionMinor: 0
};
/**
* Card networks supported by your site and your gateway
*
* #see {#link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
* #todo confirm card networks supported by your site and gateway
*/
const allowedCardNetworks = ["AMEX", "DISCOVER", "INTERAC", "JCB", "MASTERCARD", "VISA"];
/**
* Card authentication methods supported by your site and your gateway
*
* #see {#link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
* #todo confirm your processor supports Android device tokens for your
* supported card networks
*/
const allowedCardAuthMethods = ["PAN_ONLY", "CRYPTOGRAM_3DS"];
/**
* Identify your gateway and your site's gateway merchant identifier
*
* The Google Pay API response will return an encrypted payment method capable
* of being charged by a supported gateway after payer authorization
*
* #todo check with your gateway on the parameters to pass
* #see {#link https://developers.google.com/pay/api/web/reference/request-objects#gateway|PaymentMethodTokenizationSpecification}
*/
const tokenizationSpecification = {
type: 'PAYMENT_GATEWAY',
parameters: {
'gateway': 'example',
'gatewayMerchantId': 'exampleGatewayMerchantId'
}
};
/**
* Describe your site's support for the CARD payment method and its required
* fields
*
* #see {#link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
*/
const baseCardPaymentMethod = {
type: 'CARD',
parameters: {
allowedAuthMethods: allowedCardAuthMethods,
allowedCardNetworks: allowedCardNetworks
}
};
/**
* Describe your site's support for the CARD payment method including optional
* fields
*
* #see {#link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
*/
const cardPaymentMethod = Object.assign(
{},
baseCardPaymentMethod,
{
tokenizationSpecification: tokenizationSpecification
}
);
/**
* An initialized google.payments.api.PaymentsClient object or null if not yet set
*
* #see {#link getGooglePaymentsClient}
*/
let paymentsClient = null;
/**
* Configure your site's support for payment methods supported by the Google Pay
* API.
*
* Each member of allowedPaymentMethods should contain only the required fields,
* allowing reuse of this base request when determining a viewer's ability
* to pay and later requesting a supported payment method
*
* #returns {object} Google Pay API version, payment methods supported by the site
*/
function getGoogleIsReadyToPayRequest() {
return Object.assign(
{},
baseRequest,
{
allowedPaymentMethods: [baseCardPaymentMethod]
}
);
}
/**
* Configure support for the Google Pay API
*
* #see {#link https://developers.google.com/pay/api/web/reference/request-objects#PaymentDataRequest|PaymentDataRequest}
* #returns {object} PaymentDataRequest fields
*/
function getGooglePaymentDataRequest() {
const paymentDataRequest = Object.assign({}, baseRequest);
paymentDataRequest.allowedPaymentMethods = [cardPaymentMethod];
paymentDataRequest.transactionInfo = getGoogleTransactionInfo();
paymentDataRequest.merchantInfo = {
// #todo a merchant ID is available for a production environment after approval by Google
// See {#link https://developers.google.com/pay/api/web/guides/test-and-deploy/integration-checklist|Integration checklist}
// merchantId: '01234567890123456789',
merchantName: 'Example Merchant'
};
return paymentDataRequest;
}
/**
* Return an active PaymentsClient or initialize
*
* #see {#link https://developers.google.com/pay/api/web/reference/client#PaymentsClient|PaymentsClient constructor}
* #returns {google.payments.api.PaymentsClient} Google Pay API client
*/
function getGooglePaymentsClient() {
if ( paymentsClient === null ) {
paymentsClient = new google.payments.api.PaymentsClient({environment: 'TEST'});
}
return paymentsClient;
}
/**
* Initialize Google PaymentsClient after Google-hosted JavaScript has loaded
*
* Display a Google Pay payment button after confirmation of the viewer's
* ability to pay.
*/
function onGooglePayLoaded() {
const paymentsClient = getGooglePaymentsClient();
paymentsClient.isReadyToPay(getGoogleIsReadyToPayRequest())
.then(function(response) {
if (response.result) {
addGooglePayButton();
// #todo prefetch payment data to improve performance after confirming site functionality
// prefetchGooglePaymentData();
}
})
.catch(function(err) {
// show error in developer console for debugging
console.error(err);
});
}
/**
* Add a Google Pay purchase button alongside an existing checkout button
*
* #see {#link https://developers.google.com/pay/api/web/reference/request-objects#ButtonOptions|Button options}
* #see {#link https://developers.google.com/pay/api/web/guides/brand-guidelines|Google Pay brand guidelines}
*/
function addGooglePayButton() {
const paymentsClient = getGooglePaymentsClient();
const button =
paymentsClient.createButton({onClick: onGooglePaymentButtonClicked});
document.getElementById('container').appendChild(button);
}
/**
* Provide Google Pay API with a payment amount, currency, and amount status
*
* #see {#link https://developers.google.com/pay/api/web/reference/request-objects#TransactionInfo|TransactionInfo}
* #returns {object} transaction info, suitable for use as transactionInfo property of PaymentDataRequest
*/
function getGoogleTransactionInfo() {
return {
countryCode: 'US',
currencyCode: 'USD',
totalPriceStatus: 'FINAL',
// set to cart total
totalPrice: '1.00'
};
}
/**
* Prefetch payment data to improve performance
*
* #see {#link https://developers.google.com/pay/api/web/reference/client#prefetchPaymentData|prefetchPaymentData()}
*/
function prefetchGooglePaymentData() {
const paymentDataRequest = getGooglePaymentDataRequest();
// transactionInfo must be set but does not affect cache
paymentDataRequest.transactionInfo = {
totalPriceStatus: 'NOT_CURRENTLY_KNOWN',
currencyCode: 'USD'
};
const paymentsClient = getGooglePaymentsClient();
paymentsClient.prefetchPaymentData(paymentDataRequest);
}
/**
* Show Google Pay payment sheet when Google Pay payment button is clicked
*/
function onGooglePaymentButtonClicked() {
const paymentDataRequest = getGooglePaymentDataRequest();
paymentDataRequest.transactionInfo = getGoogleTransactionInfo();
const paymentsClient = getGooglePaymentsClient();
paymentsClient.loadPaymentData(paymentDataRequest)
.then(function(paymentData) {
// handle the response
processPayment(paymentData);
})
.catch(function(err) {
// show error in developer console for debugging
console.error(err);
});
}
/**
* Process payment data returned by the Google Pay API
*
* #param {object} paymentData response from Google Pay API after user approves payment
* #see {#link https://developers.google.com/pay/api/web/reference/response-objects#PaymentData|PaymentData object reference}
*/
function processPayment(paymentData) {
// show returned data in developer console for debugging
console.log(paymentData);
// #todo pass payment token to your gateway to process payment
paymentToken = paymentData.paymentMethodData.tokenizationData.token;
}
</script>
</html>
Code.gs
// https://developers.google.com/apps-script/guides/html#code.gs_1
// Use this code for Google Docs, Slides, Forms, or Sheets.
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
.createMenu('Buy report')
.addItem('Open', 'openDialog')
.addToUi();
}
function openDialog() {
const html = HtmlService.createHtmlOutputFromFile( 'Index' );
SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
.showModalDialog( html, 'Dialog title' );
// .showSidebar( html, );
}

Related

Pusher: Callback function not executing with standalone Laravel (API) Vue.js(client) apps

Please I need help with pusher integration in my Laravel & Vue js project. NOT SPA (i.e separate Apps (Laravel - API & Vuejs- frontend)
The goal is to establish a real-time chat between two users.
The whole cycle is working perfectly well but the pusher callback is not executing, therefore making the chat function limited to the app API level only. It is not real-time which is why I'm integrating pusher to handle that.
Please see the code snippets below, ready to provide more on request. I've spent days on this, still can't figure out what I'm doing wrong.
Thanks in anticipation.
CommentController
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$user = Auth::user();
$validator = Validator::make($request->all(), [
'bid_id' => 'required',
'message' => ['required', 'string'],
]);
if ($validator->fails()) {
return response()->json(["error" => $validator->errors()], 400);
}
try {
$comment = $user->comments()->create([
'bid_id' => $request->bid_id,
'message' => $request->message,
]);
// Fire the comment broadcast event
// event(new CommentEvent($comment));
broadcast(new CommentEvent($user, $comment->load('user')))->toOthers();
} catch (Exception $exception) {
Log::error("Error while creating Comment" . $exception->getMessage());
} finally {
return response()->json(['comment' => $comment], 201);
}
}
CommentEvent.php
<?php
namespace App\Events;
use App\Models\Comment;
use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class CommentEvent implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* #param $comment
*
* #return void
*/
public $user;
public $comment;
public function __construct(User $user, Comment $comment)
{
$this->user = $user;
$this->comment = $comment;
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PresenceChannel('comment-channel');
}
public function broadcastAs()
{
return 'CommentEvent';
}
}
Main.js
import Pusher from "pusher-js";
/* -------------------------------------------------------------------------- */
/* PUSHER CONFIG */
/* -------------------------------------------------------------------------- */
Pusher.logToConsole = true;
let pusher = new Pusher(process.env.VUE_APP_PUSHER_APP_KEY,{
cluster: process.env.VUE_APP_PUSHER_APP_CLUSTER,
encrypted: false,
});
Vue.prototype.$pusher = pusher;
App.vue
<script>
export default {
name: "App",
components: {},
created() {
let channel = this.$pusher.subscribe("comment-channel");
channel.bind("pusher:subscription_succeeded", function(members) {
console.log(members);
console.log("succesfully subscribed!");
});
channel.bind("CommentEvent", function(data) {
console.log(data);
this.$store.commit("ADD_COMMENT", data.comment);
});
},
methods: {
},
};
</script>
I've been able to resolve this using.
but I had to switch tech. The real-time chat system of my app is now driven by socket.io, Redis and a simple node js server wrapped within the API
I'll be willing to help with code snippets if you need me to.

There are no accepted cards available for use with this merchant - Google Pay

I am trying to integrate Google Pay web into my website but when i click "pay with googlepay" its shows the below error:
There are no accepted cards available for use with this merchant.
When i read documentation it says you can add example as merchant for testing, I just wanted to use test environment but still its not working.
Here is the code that i am using:
const allowedAuthMethods = ['PAN_ONLY','CRYPTOGRAM_3DS'] ;
const baseCardPaymentMethod = {
type: 'CARD',
parameters: {
allowedCardNetworks: allowedNetworks,
allowedAuthMethods: allowedAuthMethods
}
};
const googlePayBaseConfiguration = {
apiVersion: 2,
apiVersionMinor: 0,
allowedPaymentMethods: [baseCardPaymentMethod]
};
/**
* Holds the Google Pay client used to call the different methods available
* through the API.
* #type {PaymentsClient}
* #private
*/
let googlePayClient;
/**
* Defines and handles the main operations related to the integration of
* Google Pay. This function is executed when the Google Pay library script has
* finished loading.
*/
function onGooglePayLoaded() {
googlePayClient = new google.payments.api.PaymentsClient({
environment: 'TEST'
});
googlePayClient.isReadyToPay(googlePayBaseConfiguration)
.then(function(response) {
if(response.result) {
createAndAddButton();
} else {
alert("Unable to pay using Google Pay");
}
}).catch(function(err) {
console.error("Error determining readiness to use Google Pay: ", err);
});
}
/**
* Handles the creation of the button to pay with Google Pay.
* Once created, this button is appended to the DOM, under the element
* 'buy-now'.
*/
function createAndAddButton() {
const googlePayButton = googlePayClient.createButton({
// currently defaults to black if default or omitted
buttonColor: 'default',
// defaults to long if omitted
buttonType: 'long',
onClick: onGooglePaymentsButtonClicked
});
document.getElementById('buy-now').appendChild(googlePayButton);
}
/**
* Handles the click of the button to pay with Google Pay. Takes
* care of defining the payment data request to be used in order to load
* the payments methods available to the user.
*/
function onGooglePaymentsButtonClicked() {
const tokenizationSpecification = {
type: 'PAYMENT_GATEWAY',
parameters: {
gateway: 'example',
gatewayMerchantId: 'exampleGatewayMerchantId'
}
};
const cardPaymentMethod = {
type: 'CARD',
tokenizationSpecification: tokenizationSpecification,
parameters: {
allowedCardNetworks: ['VISA','MASTERCARD'],
allowedAuthMethods: ['PAN_ONLY','CRYPTOGRAM_3DS'],
billingAddressRequired: true,
billingAddressParameters: {
format: 'FULL',
phoneNumberRequired: true
}
}
};
const transactionInfo = {
totalPriceStatus: 'FINAL',
totalPrice: '123.45',
currencyCode: 'USD',
countryCode: 'US'
};
const merchantInfo = {
merchantId: '01234567890123456789', //Only in PRODUCTION
merchantName: 'Example Merchant Name'
};
const paymentDataRequest = Object.assign({}, googlePayBaseConfiguration, {
allowedPaymentMethods: [cardPaymentMethod],
transactionInfo: transactionInfo,
merchantInfo: merchantInfo
});
googlePayClient
.loadPaymentData(paymentDataRequest)
.then(function(paymentData) {
processPayment(paymentData);
}).catch(function(err) {
// Log error: { statusCode: CANCELED || DEVELOPER_ERROR }
});
}
function processPayment(paymentData) {
// TODO: Send a POST request to your processor with the payload
// https://us-central1-devrel-payments.cloudfunctions.net/google-pay-server
// Sorry, this is out-of-scope for this codelab.
return new Promise(function(resolve, reject) {
// #todo pass payment token to your gateway to process payment
const paymentToken = paymentData.paymentMethodData.tokenizationData.token;
console.log('mock send token ' + paymentToken + ' to payment processor');
setTimeout(function() {
console.log('mock response from processor');
alert('done');
resolve({});
}, 800);
});
} ```
There are no accepted cards available for use with this merchant.
This message means that the current Google user doesn't have any cards that are compatible with the payment options that the merchant has provided. Specifically allowedCardNetworks and allowedAuthMethods.
Here is a JSFiddle that I created based on your snippet: https://jsfiddle.net/aumg6ncb/
This is what I get back after clicking on the button:
If You are using Testing mode:-
I think You used testing card on your Chrome browser or Google Wallet
When testing Google Pay you should have a real card saved in your Chrome browser or Google Wallet, and have your test API keys/test Google Pay environment active. The real card does not get charged, and Google passes a test card during the checkout flow instead of a real card. Our normal test cards do not work with Google Pay when the user tries to save them in Chrome

Laravel, Vue.js array empty

I'm totally new to the laravel and vue.js frameworks. For a school project with have to create an app that uses laravel for the back-end and vue.js for the front-end. So I created an API to use it afterwards in my vue.Js. But the problem is that when I want to get the data in my Vue.JS it tells me it has an empty array which should show all the values I have in my database (about 20). I have been struggling since 2 days on it and now, I'm totally lost, I don't know what to do or what goes wrong.. I tried my API into postman and it works perfect.
Specs:
-> Laravel 5.6
-> Vue 2.5.7
My API Controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Note;
use App\Http\Resources\Note as NoteResource;
class NotesApiController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$notes = Note::all();
return NoteResource::collection($notes);
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$note = new Note;
$note->user_id = $request -> input('user_id');
$note->course_id = $request -> input('course_id');
$note->title = $request -> input('title');
$note->body = $request -> input('body');
$note->file_name = $request -> input('file_name');
$note->save();
return new NoteResource($note);
return response() -> json('success', 200);
}
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show($id)
{
$note = Note::findOrFail($id);
return new NoteResource($note);
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$note = Note::find($id);
$note->user_id = $request -> input('user_id');
$note->course_id = $request -> input('course_id');
$note->title = $request -> input('title');
$note->body = $request -> input('body');
$note->file_name = $request -> input('file_name');
$note->save();
return new NoteResource($note);
return response() -> json('success', 200);
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function destroy($id)
{
$note = Note::find($id);
$note -> delete();
return response() -> json('success', 200);
}
}
My Note Resource:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class Note extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'user_id' => $this->user_id,
'course_id' => $this->course_id,
'title' => $this->title,
'body' => $this->body,
'file_name' => $this->file_name,
'created_at' => $this->created_at->format('d/m/Y'),
'updated_at' => $this->updated_at->diffForHumans(),
];
}
}
API Routes:
<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
// Route::middleware('auth:api')->get('/user', function (Request $request) {
// return $request->user();
// });
//Index Page Vue
Route::get('/', 'PagesControllerApi#index');
//Afficher la liste de toutes les notes
Route::get('notes', 'NotesApiController#index');
//Montre une note selon son id
Route::get('notes/{id}', 'NotesApiController#show');
//Enregistre la note
Route::post('notes', 'NotesApiController#store');
//Mise-à-jour d'une note
Route::put('notes/{id}', 'NotesApiController#update');
//Effacer une note
Route::delete('notes/{id}', 'NotesApiController#destroy');
APP.JS of Vue:
/**
* First we will load all of this project's JavaScript dependencies which
* includes Vue and other libraries. It is a great starting point when
* building robust, powerful web applications using Vue and Laravel.
*/
require('./bootstrap');
window.Vue = require('vue');
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
Vue.component('notes', require('./components/notes.vue'));
Vue.component('navbar', require('./components/navbar.vue'));
Vue.component('paginate', require('vuejs-paginate'));
const app = new Vue({
el: '#app'
});
My Note Component in Vue
<template>
<div>
<h2 class="m-4">Notes</h2>
</div>
</template>
<script>
export default {
data() {
return{
notes: [],
note: {
id: '',
user_id: '',
course_id: '',
title: '',
body: '',
file_name:'',
},
note_id: '',
pagination: {},
edit: false
};
},
created() {
this.fetchArticles();
},
methods: {
fetchArticles(page_url) {
let vm = this;
page_url = page_url || '/api/notes';
fetch(page_url)
.then(res => res.json())
.then(res => {
this.articles = res.data;
})
.catch(err => console.log(err));
},
}
}
</script>
index.blade.php
<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<script>window.Laravel = { csrfToken: "{{ csrf_token() }}" }</script>
<title>{{ config("app.name", "Laravel") }}</title>
<!-- Scripts -->
<script src="{{ asset('js/app.js') }}" defer></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<!-- Fonts -->
<link rel="dns-prefetch" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css?family=Raleway:300,400,600" rel="stylesheet" type="text/css">
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
<div id="app">
<navbar></navbar>
<div class="container">
<notes>
</notes>
</div>
</div>
</body>
</html>
API
Results
What the VUE Develope shows
Empty Array
Because of that I can't show any value on my page..
Thanks in advance!
It should be this.notes = res.data but you have this.articles = res.data;
Then you just need to loop over the array and access properties on the object, something like this:
<div v-for="(note, index) in notes" :key="`note-${key}`">
<h2 v-text="note.title"></h2>
<p v-text="note.body"></p>
<p v-text="note.filename"></p>
</div>

How to document existing nodejs APIs?

New to Node, and really frustrated with multiple libraries to do the same thing. I am going no where.
I have existing Node + express application, and need to integrate swagger docs.
Most useful thread I found was this one. But it just expects to have parallel api-docs.json file. This is not generated automatically.
I want to document each API in the controller, and want to generate the documents. Can someone point me to better resource?
Finally I was able to make it work with swagger-jsdoc
Steps
Add Swagger-UI package
Add swagger-ui dependency
npm install -g swagger-ui
Configure swagger in your server.js file
var swaggerJSDoc = require('swagger-jsdoc');
var options = {
swaggerDefinition: {
info: {
title: 'Hello World', // Title (required)
version: '1.0.0', // Version (required)
},
},
apis: ['./routes.js'], // Path to the API docs
};
var swaggerSpec = swaggerJSDoc(options);
app.get('/api-docs.json', function(req, res) {
res.setHeader('Content-Type', 'application/json');
res.send(swaggerSpec);
});
app.use('/api/docs',express.static('./node_modules/swagger-ui/dist'));
Add swagger comments to your controller functions
/**
* #swagger
* resourcePath: /api
* description: All about API
*/
/**
* #swagger
* /login:
* post:
* description: Login to the application
* produces:
* - application/json
* parameters:
* - name: username
* description: Username to use for login.
* in: formData
* required: true
* type: string
* - name: password
* description: User's password.
* in: formData
* required: true
* type: string
* responses:
* 200:
* description: login
*/
app.post('/login', function(req, res) {
res.json(req.body);
});
Swagger documentation available at
http://localhost:3000/api/docs/?url=/api-docs.json

"GAPI is not defined" message

I'm trying to use the Google Sheets API for inclusion in my web app, but I keep receiving a error specifying that the gapi library is not defined. I've tried delay the request to the server by using the ComponentDidMount life cycle method, and even using a timeout in that method, but I keep receiving the same error. How can I have the gapi library defined for use then in my app ?
import React from 'react';
var CLIENT_ID = '';
var SCOPES = ["https://www.googleapis.com/auth/spreadsheets.readonly"];
export default class MyNavbar extends React.Component {
constructor(props) {
super(props);
}
componentDidMount(){
this.checkAuth();
}
/**
* Check if current user has authorized this application.
*/
checkAuth(){
gapi.auth.authorize(
{
'client_id': CLIENT_ID,
'scope': SCOPES.join(' '),
'immediate': true
}, this.handleAuthResult());
}
/**
* Handle response from authorization server.
*
* #param {Object} authResult Authorization result.
*/
handleAuthResult(authResult) {
var authorizeDiv = document.getElementById('authorize-div');
if (authResult && !authResult.error) {
// Hide auth UI, then load client library.
authorizeDiv.style.display = 'none';
loadSheetsApi();
} else {
// Show auth UI, allowing the user to initiate authorization by
// clicking authorize button.
authorizeDiv.style.display = 'inline';
}
}
/**
* Initiate auth flow in response to user clicking authorize button.
*
* #param {Event} event Button click event.
*/
handleAuthClick(event) {
gapi.auth.authorize(
{client_id: CLIENT_ID, scope: SCOPES, immediate: false},
handleAuthResult);
return false;
}
/**
* Load Sheets API client library.
*/
loadSheetsApi() {
var discoveryUrl =
'https://sheets.googleapis.com/$discovery/rest?version=v4';
gapi.client.load(discoveryUrl).then(listMajors);
}
/**
* Print the names and majors of students in a sample spreadsheet:
* https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
*/
listMajors() {
gapi.client.sheets.spreadsheets.values.get({
spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms',
range: 'Class Data!A2:E',
}).then(function(response) {
var range = response.result;
if (range.values.length > 0) {
appendPre('Name, Major:');
for (i = 0; i < range.values.length; i++) {
var row = range.values[i];
// Print columns A and E, which correspond to indices 0 and 4.
appendPre(row[0] + ', ' + row[4]);
}
} else {
appendPre('No data found.');
}
}, function(response) {
appendPre('Error: ' + response.result.error.message);
});
}
/**
* Append a pre element to the body containing the given message
* as its text node.
*
* #param {string} message Text to be placed in pre element.
*/
appendPre(message) {
var pre = document.getElementById('output');
var textContent = document.createTextNode(message + '\n');
pre.appendChild(textContent);
}
render(){
return (
<div>
<h1>Hello World My Name Is Justin 2</h1>
<div id="authorize-div"></div>
<pre id="output"></pre>
</div>
);
}
}
Here try this.
import React from 'react';
import asyncLoad from 'react-async-loader'; // for loading script tag asyncly `npm i --save react-async-loader`
const CLIENT_ID = '';
const SCOPES = ["https://www.googleapis.com/auth/spreadsheets.readonly"];
// For making gapi object passed as props to our component
const mapScriptToProps = state => ({
// gapi will be this.props.gapi
gapi: {
globalPath: 'gapi',
url: 'https://your-gapi-url'
}
});
#asyncLoad(mapScriptToProps)
class MyNavbar extends React.Component {
constructor(props) {
super(props);
this.gapi = null;
// You need to bind methods to this class's object context. (i.e this)!
this.checkAuth = this.checkAuth.bind(this);
this.handleAuthResult = this.authResult.bind(this);
this.handleAuthClick = this.handleAuthClick.bind(this);
this.loadSheetsApi = this.loadSheetsApi.bind(this);
this.listMajors = this.listMajors.bind(this);
}
componentDidMount() {
// Check is gapi loaded?
if (this.props.gapi !== null) {
this.checkAuth();
}
}
componentWillReceiveProps({ gapi }) {
if (gapi!== null) {
this.checkAuth();
}
}
/**
* Check if current user has authorized this application.
*/
checkAuth() {
// this will give you an error of gapi is not defined because there is no
// reference of gapi found globally you cannot access global object which are
// not predefined in javascript( in this case its window.gapi ).
// properties added by Programmer cannot be accessed directly( if it's not in the same file as well as the same scope!) in commonjs modules. Because they
// don't' run in a global scope. Any variable in another module which is not
// exported, will not be available to other modules.
this.gapi = window.gapi; // you can do like this. Now you can access gapi in all methods if this class.
this
.gapi
.auth
.authorize({
'client_id': CLIENT_ID,
'scope': SCOPES.join(' '),
'immediate': true
}, this.handleAuthResult());
}
/**
* Handle response from authorization server.
*
* #param {Object} authResult Authorization result.
*/
handleAuthResult(authResult) {
var authorizeDiv = document.getElementById('authorize-div');
if (authResult && !authResult.error) {
// Hide auth UI, then load client library.
authorizeDiv.style.display = 'none';
loadSheetsApi();
} else {
// Show auth UI, allowing the user to initiate authorization by clicking
// authorize button.
authorizeDiv.style.display = 'inline';
}
}
/**
* Initiate auth flow in response to user clicking authorize button.
*
* #param {Event} event Button click event.
*/
handleAuthClick(event) {
// gapi.auth.authorize( here also gapi is not defined
this
.gapi
.auth
.authorize({
client_id: CLIENT_ID,
scope: SCOPES,
immediate: false
}, handleAuthResult);
return false;
}
/**
* Load Sheets API client library.
*/
loadSheetsApi() {
var discoveryUrl = 'https://sheets.googleapis.com/$discovery/rest?version=v4';
// also will give your error
// for gapi being not defined.
// gapi.client.load(discoveryUrl).then(listMajors);
this
.gapi
.client
.load(discoveryUrl)
.then(listMajors);
}
/**
* Print the names and majors of students in a sample spreadsheet:
* https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
*/
listMajors() {
this.gapi
.client
.sheets
.spreadsheets
.values
.get({spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms', range: 'Class Data!A2:E'})
.then(function (response) {
var range = response.result;
if (range.values.length > 0) {
appendPre('Name, Major:');
for (i = 0; i < range.values.length; i++) {
var row = range.values[i];
// Print columns A and E, which correspond to indices 0 and 4.
appendPre(row[0] + ', ' + row[4]);
}
} else {
appendPre('No data found.');
}
}, function (response) {
appendPre('Error: ' + response.result.error.message);
});
}
/**
* Append a pre element to the body containing the given message
* as its text node.
*
* #param {string} message Text to be placed in pre element.
*/
appendPre(message) {
// as you can see. You are accessing window.document as document.
// its fine because its defined in javascript (implicitly),
// not explicitly by programmer(here you!).
var pre = document.getElementById('output');
var textContent = document.createTextNode(message + '\n');
pre.appendChild(textContent);
}
render() {
return (
<div>
<h1>Hello World My Name Is Justin 2</h1>
<div id="authorize-div"></div>
<pre id="output"></pre>
</div>
);
}
}
export default MyNavbar;
You need to load the gapi library before bundle.js in index.html file, or better you can load the gapi js script async with react-async-loader.
Here's how you can do it :
import React, { Component, PropTypes } from 'react';
import asyncLoad from 'react-async-loader'; // for loading script tag asyncly
// For making gapi object passed as props to our component
const mapScriptToProps = state => ({
gapi: {
globalPath: 'gapi',
url: 'https://your-gapi-url'
}
});
// decorate our component
#asyncLoad(mapScriptToProps)
class yourComponent extends Component {
componentDidMount() {
// Check is gapi loaded?
if (this.props.gapi !== null) {
this.checkAuth();
}
}
componentWillReceiveProps({ gapi }) {
if (gapi!== null) {
this.checkAuth();
}
}
checkAuth = () => {
// Better check with window and make it available in component
this.gapi = window.gapi;
this.gapi.auth.authorize({
'client_id': CLIENT_ID,
'scope': SCOPES.join(' '),
'immediate': true
}, this.handleAuthResult);
}
handleAuthResult = (authData) => {
// Your auth loagic...
}
render() {
return ( <div>
{ this.props.gapi &&
<YourSpreadsheetOrWhatever data={ this.getData() } />
}
</div>)
}
}
Try replacing your code with this and check if it works.
I found this project https://github.com/rlancer/gapi-starter it may be helpful.
Google Login & API + ReactJS + Flow + Webpack starter kit
Google API's are great but they were designed before the module partner of Javascript programing became popular. This starter fixes that handing Google login and library loading for you.
Get the Code!
git clone https://github.com/rlancer/gapi-starter.git
cd gapi-starter
npm install
You need to load the gapi library before bundle.js in index.html file as :
<script src="https://apis.google.com/js/api.js"></script>
And then you just need to initialize your gapi variablesas :
let gapi = window.gapi;

Categories

Resources