Getting Firebase Functions Internal Error - javascript

I am trying to implement Razorpay Payment Integration with Firebase Cloud Functions API in Android. Every is working fine but when verifying the payment on the server Firebase Functions Throw an Internal Error as FirebaseFunctionsEceptions.InternalError.
This is my server side code
exports.confirmPayment = functions.https.onCall((data, context) => {
if (context.auth) {
const amount = data.amount;
const text = data.razorpay_order_id + "|" + data.razorpay_payment_id;
var signature = crypto
.createHmac("sha256", key_secret)
.update(text)
.digest("hex");
if (signature === data.razorpay_signature) {
console.log("PAYMENT SUCCESSFUL");
return { text: "PAYMENT_SUCCESSFUL" };
} else {
throw new functions.https.HttpsError('Invalid Pack Bought', 'Your payment was unsuccessful due to invalid pack purchasing!')
}
}else{
throw new functions.https.HttpsError('Unauthenticated User', 'Unable to confirm Payment!')
}
});
And my Android Studio Java Code is
When Payment Is Successful Razorpay's onPaymentSuccess is thrown and after the payment I have to verify if the payment made is authentic and has same amount. which is done by verifyPayment method which is given below
#Override
public void onPaymentSuccess(String s, PaymentData paymentData) {
Toast.makeText(this, "Payment Successful verifying now!", Toast.LENGTH_SHORT).show();
Task<String> results = verifyPayment(paymentData);
results.addOnCompleteListener(new OnCompleteListener<String>() {
#Override
public void onComplete(#NonNull Task<String> task) {
if (task.isSuccessful()) {
Snackbar.make(btnBuy.getRootView(), "Points have been added to your balance!", Snackbar.LENGTH_SHORT).show();
} else {
Toast.makeText(BuyPointsActivity.this, "" + task.getException(), Toast.LENGTH_LONG).show();
}
}
});
}
Payment Verification Method is Called Via This Method
private Task<String> verifyPayment(PaymentData data) {
Map<String, Object> data1 = new HashMap<>();
data1.put("razorpay_payment_id", data.getPaymentId());
data1.put("razorpay_order_id", data.getOrderId());
data1.put("amount", amount);
data1.put("razorpay_signature", data.getSignature());
data1.put("push", true);
return functions
.getHttpsCallable("confirmPayment")
.call(data1)
.continueWith(new Continuation<HttpsCallableResult, String>() {
#Override
public String then(#NonNull Task<HttpsCallableResult> task) throws Exception {
// This continuation runs on either success or failure, but if the task
// has failed then getResult() will throw an Exception which will be
// propagated down.
String result = (String) task.getResult().getData();
return result;
}
});
}
This is the Cloud Functions Log
confirmPayment
{"#type":"type.googleapis.com/google.cloud.audit.AuditLog","authenticationInfo":{"principalEmail":"mmbestonbest#gmail.com"},"requestMetadata":{"callerIp":"47.30.174.168","callerSuppliedUserAgent":"FirebaseCLI/8.0.1,gzip(gfe),gzip(gfe)","requestAttributes":{"time":"2020-04-05T03:49:42.041Z","auth":{}},"destinationAttributes":{}},"serviceName":"cloudfunctions.googleapis.com","methodName":"google.cloud.functions.v1.CloudFunctionsService.UpdateFunction","authorizationInfo":[{"resource":"projects/beingfit-6c699/locations/us-central1/functions/confirmPayment","permission":"cloudfunctions.functions.update","granted":true,"resourceAttributes":{}}],"resourceName":"projects/beingfit-6c699/locations/us-central1/functions/confirmPayment","request":{"updateMask":"sourceUploadUrl,name,labels,runtime,httpsTrigger","#type":"type.googleapis.com/google.cloud.functions.v1.UpdateFunctionRequest","function":{"runtime":"nodejs8","name":"projects/beingfit-6c699/locations/us-central1/functions/confirmPayment","httpsTrigger":{},"labels":{"deployment-callable":"true","deployment-tool":"cli-firebase"},"sourceUploadUrl":"https://storage.googleapis.com/gcf-upload-us-central1-2535687d-781c-43d3-8bb1-6cdc1434531f/0cdb81e8-551c-4de7-83c0-8fe50f5a97bb.zip?GoogleAccessId=service-615941328944#gcf-admin-robot.iam.gserviceaccount.com&Expires=1586060376&Signature=rF3FqDJFIbAXSLK8DiemM6X%2BfTvMhv6RMpSvFCaHFjSfQ1kEf%2B5jV92LColV1k0szPt98HsExV2u2HGHRBSobobloyY8oAyh94WYHQTkVQZfhRGCOTvSliuhIhE98kRLVLOEPOjkyhNlz1v9kz48z1tq%2BRYyFB224joiU6qx4Qa58a08QZ3a1Ty4yQAuYedz2lgj3auKoJj10MzGf9T9YW3ffvN9vy4fF1z10erVaNMspgrZoU43NnJGWt%2B185ZRT9Gx%2B6mfxYinkXlfeVj3b8WOKwcYGbtN1FnFaUf8wGX3fAb%2B6FRB2n5ATIchyNHnr1F7E6sbdpmG5cCDZE1kbQ%3D%3D"}},"resourceLocation":{"currentLocations":["us-central1"]}}
9:20:10.642 AM
confirmPayment
{"#type":"type.googleapis.com/google.cloud.audit.AuditLog","status":{},"authenticationInfo":{"principalEmail":"mmbestonbest#gmail.com"},"serviceName":"cloudfunctions.googleapis.com","methodName":"google.cloud.functions.v1.CloudFunctionsService.UpdateFunction","resourceName":"projects/beingfit-6c699/locations/us-central1/functions/confirmPayment"}
9:21:23.540 AM
confirmPayment
{"#type":"type.googleapis.com/google.cloud.audit.AuditLog","authenticationInfo":{"principalEmail":"mmbestonbest#gmail.com"},"requestMetadata":{"callerIp":"47.30.174.168","callerSuppliedUserAgent":"FirebaseCLI/8.0.1,gzip(gfe),gzip(gfe)","requestAttributes":{"time":"2020-04-05T03:51:23.614Z","auth":{}},"destinationAttributes":{}},"serviceName":"cloudfunctions.googleapis.com","methodName":"google.cloud.functions.v1.CloudFunctionsService.UpdateFunction","authorizationInfo":[{"resource":"projects/beingfit-6c699/locations/us-central1/functions/confirmPayment","permission":"cloudfunctions.functions.update","granted":true,"resourceAttributes":{}}],"resourceName":"projects/beingfit-6c699/locations/us-central1/functions/confirmPayment","request":{"updateMask":"sourceUploadUrl,name,labels,runtime,httpsTrigger","#type":"type.googleapis.com/google.cloud.functions.v1.UpdateFunctionRequest","function":{"sourceUploadUrl":"https://storage.googleapis.com/gcf-upload-us-central1-2535687d-781c-43d3-8bb1-6cdc1434531f/87426444-4523-4584-a501-e219ac62e36e.zip?GoogleAccessId=service-615941328944#gcf-admin-robot.iam.gserviceaccount.com&Expires=1586060478&Signature=g2AJztAxLbvci2q7MDms1K7%2B466Z2RM6ALQmeBdH%2By7AA3hMAkdKSI7B3FzY8g1m%2BBuMlO8fkCWBSXcgHauxsOni%2FNW1LD7MZ%2FSUSkZ4npE%2FyVNRy0aitEzVSo6GaPmV9415KoFADyXHAxNakuD8JPZ4wl7qn%2FdcmQ8cZRJ%2BX%2BZ7w2xRw6BmG%2Fbnnmy8DCZe%2F%2Fw%2BIBhM4bpSOO8Ne8nMDa7qAfMzMXHHU8PDuIuIqdOmdIG0xF0itGsw3bhvAc530xWcJY%2F%2B7u%2FUjaszVikTV9igU33nUsqA5%2FzuNLmXrR9B42gn1ncCiwjnx8rlrcETmKkMrD6%2FABM5nYEQ%2Fvy%2BRw%3D%3D","runtime":"nodejs8","name":"projects/beingfit-6c699/locations/us-central1/functions/confirmPayment","httpsTrigger":{},"labels":{"deployment-tool":"cli-firebase","deployment-callable":"true"}}},"resourceLocation":{"currentLocations":["us-central1"]}}
9:21:56.574 AM
confirmPayment
{"#type":"type.googleapis.com/google.cloud.audit.AuditLog","status":{},"authenticationInfo":{"principalEmail":"mmbestonbest#gmail.com"},"serviceName":"cloudfunctions.googleapis.com","methodName":"google.cloud.functions.v1.CloudFunctionsService.UpdateFunction","resourceName":"projects/beingfit-6c699/locations/us-central1/functions/confirmPayment"}
11:45:07.349 AM
confirmPayment
{"#type":"type.googleapis.com/google.cloud.audit.AuditLog","authenticationInfo":{"principalEmail":"mmbestonbest#gmail.com"},"requestMetadata":{"callerIp":"47.30.162.219","callerSuppliedUserAgent":"FirebaseCLI/8.0.1,gzip(gfe),gzip(gfe)","requestAttributes":{"time":"2020-04-05T06:15:07.712Z","auth":{}},"destinationAttributes":{}},"serviceName":"cloudfunctions.googleapis.com","methodName":"google.cloud.functions.v1.CloudFunctionsService.UpdateFunction","authorizationInfo":[{"resource":"projects/beingfit-6c699/locations/us-central1/functions/confirmPayment","permission":"cloudfunctions.functions.update","granted":true,"resourceAttributes":{}}],"resourceName":"projects/beingfit-6c699/locations/us-central1/functions/confirmPayment","request":{"updateMask":"sourceUploadUrl,name,labels,runtime,httpsTrigger","#type":"type.googleapis.com/google.cloud.functions.v1.UpdateFunctionRequest","function":{"runtime":"nodejs8","name":"projects/beingfit-6c699/locations/us-central1/functions/confirmPayment","httpsTrigger":{},"labels":{"deployment-callable":"true","deployment-tool":"cli-firebase"},"sourceUploadUrl":"https://storage.googleapis.com/gcf-upload-us-central1-2535687d-781c-43d3-8bb1-6cdc1434531f/82b41c31-8b5c-4cd4-857c-301b4f554dd5.zip?GoogleAccessId=service-615941328944#gcf-admin-robot.iam.gserviceaccount.com&Expires=1586069101&Signature=FNLT8XrMwerp00RaqJXMPj6qk7Ztawx0NSAYJIWnWsMy0xdvB0bgqUrYx0dUYGx38r1c6xtyN7mSqFEXEN4IDOkDqWCRFDibiABDmTG9J60F2822bCeReGyYvmTJGVA%2BvBELdtvoiSBaVFHHYQfQdCKGKObboW2950zEeuq52pk30M3doDCUoXuGQUDSpQrZnlXLabH6JjvPDDj6VYcF42HnUmnesd43yErky6ZiW2qo6F4nGAj9hwEDAKFJHX%2FoW1HLDdMjQ2EI0t9%2FPRBi6fvtNjLqbAtE7xjQSwvDlyU1uMFAqwVC5RxKeTGM195E6RbKlSY6YsEzp4rAWltU%2Fw%3D%3D"}},"resourceLocation":{"currentLocations":["us-central1"]}}
11:45:32.142 AM
confirmPayment
{"#type":"type.googleapis.com/google.cloud.audit.AuditLog","status":{},"authenticationInfo":{"principalEmail":"mmbestonbest#gmail.com"},"serviceName":"cloudfunctions.googleapis.com","methodName":"google.cloud.functions.v1.CloudFunctionsService.UpdateFunction","resourceName":"projects/beingfit-6c699/locations/us-central1/functions/confirmPayment"}
Logs are subject to Cloud Logging's

Related

Differentiating Types of API (Apex) Exceptions In JavaScript Try/Catch

In a Lightning Web Component, I'm making an API call to an Apex method, which can throw an exception. Currently, in the catch block, there's code checking for a specific string in the exception, b/c if it's a certain type of exception we want to display a certain message to the user; otherwise we want to display another generic message. Obviously determining the specific exception by string is not robust.
Is there a good way in JavaScript to determine which specific Apex exception was thrown?
I noticed that the error object that is currently being passed to the catch block does include a headers property, and I'm wondering if I could pass a custom value in there like exceptionType? Something like:
async myFunc() {
try {
const response = await myApexMethodCall();
} catch(err) {
if (err.headers.exceptionType === 'E123') {
alert('The order must be associated with a case before processing');
} else {
alert('There was an error with the API call. Please contact your administrator');
}
}
}
You can create a custom class which represents the response. For instance,
public with sharing class CustomOrderStatusResponse {
#AuraEnabled
public String errorCode { get; set; }
#AuraEnabled
public Map<String, String> result { get; set; }
public CustomOrderStatusResponse(String errorCode, Map<String, String> result) {
this.errorCode = errorCode;
this.result = result;
}
}
And, then in controller class, you can create instance of CustomOrderStatusResponse based on different scenarios and can set differnet values for errorCode. For instance,
public with sharing class CustomOrderStatus {
#AuraEnabled
public static CustomOrderStatusResponse getOrderStatus(String orderId){
CustomOrderStatusResponse response;
try {
if (orderId.equals('123')) {
Map<String, String> result = new Map<String, String>();
result.put('status', 'completed');
response = new CustomOrderStatusResponse(null, result);
} else if (orderId.equals('456')) {
response = new CustomOrderStatusResponse('E123', null);
} else if (orderId.equals('789')) {
response = new CustomOrderStatusResponse('E789', null);
}
return response;
} catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
}
In frontend (LWC), you can do something like this:
import { LightningElement } from 'lwc';
import getOrderStatus from "#salesforce/apex/CustomOrderStatus.getOrderStatus";
export default class OrderStatus extends LightningElement {
handleFirstClick(event) {
this.displayOrderStatus('123');
}
handleSecondClick(event) {
this.displayOrderStatus('456');
}
handleThirdClick(event) {
this.displayOrderStatus('789');
}
displayOrderStatus(orderId) {
getOrderStatus({ orderId: orderId })
.then(response => {
if (response.errorCode === 'E123') {
alert('The order must be associated with a case before processing');
} else if (response.errorCode) {
alert('There was an error with API Call');
} else {
alert('Status: ' + response.result.status);
}
})
.catch(console.error);
}
}
Reference: https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.apex_wire_method and https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.apex_call_imperative
I hope this answers your question. Any suggestions and comments are welcome.

What is a way to return exception caught in asp.net Web Api to Angular 8?

I have my angular service like:
GetPaymentDeadlineExtension(data: PatientInput): Observable<any> {
return this.httpClient.post<any>(
this.root + `/api/PaymentDeadline/RegisterPatientInput`,data);
}
in my web api controller:
[Route("RegisterPatientInput")]
public SrvInvoiceCompositView[] RegisterGetPaymentDeadlineExtension(PatientInput data)
{
SrvInvoiceCompositView[] list = null;
string ContractNo = String.Empty;
string DktInvoiceNo = String.Empty;
try
{
ContractNo = data.BillNumber.Split('-')[0];
DktInvoiceNo = data.BillNumber.Split('-')[1];
list = DkService.SrvFindInvoiceCompositViewDentaPay(DktInvoiceNo, data.Amount, data.PatientNumber, ContractNo);
}
catch(Exception ex)
{
// exception to be returned to angular app.
}
return list;
}
and in my component.ts file:
this.homePageService.GetPaymentDeadlineExtension(this.input).subscribe(
data=>
{
this.patientInfo = data;
},
error=>
{
//i want to get my ex.message here, so i can display it
}
)
this.homePageService.GetPaymentDeadlineExtension(this.input)
.pipe(catchError((error)=> { handleError here}).subscribe(
data=>
{
this.patientInfo = data;
}
)
More info: https://www.learnrxjs.io/learn-rxjs/operators/error_handling/catch
To get an error in RxJS subscribe method you have to return such a http response that doesn't have 2xx status code. Based on your code snippet Bad Request (400) could be a good choice for example. So on .NET Core side you need to return an IActionResult following way:
[Route("RegisterPatientInput")]
public IActionResult RegisterGetPaymentDeadlineExtension(PatientInput data)
{
SrvInvoiceCompositView[] list = null;
string ContractNo = String.Empty;
string DktInvoiceNo = String.Empty;
try
{
ContractNo = data.BillNumber.Split('-')[0];
DktInvoiceNo = data.BillNumber.Split('-')[1];
list = DkService.SrvFindInvoiceCompositViewDentaPay(DktInvoiceNo, data.Amount, data.PatientNumber, ContractNo);
return Ok(list); // This is has http status code 200 in the http response
}
catch(Exception ex)
{
return BadRequest(ex.Message); // This has http status code 400 in the http response
}
}
If you put in the Angular Component file following:
this.homePageService.GetPaymentDeadlineExtension(this.input).subscribe(
data=>
{
this.patientInfo = data;
},
error=>
{
console.log(error);
}
)
After a http request you will be able to see the exception message from the backend in the browser console.
I don't know exactly the use case of the developed software, but it is not recommended to display the exception on the frontend side in production. A hacker could extract some information from the server code with causing intentionally exceptions.

How to throw HttpsError in onCall() function and catch on android client

I'm trying to create a stripe charge using a cloud function, and I only receive INTERNAL exception on the client. Where and how should the HttpsError be thrown to be caught by an android client.
I followed the cloud functions documentation found here, but when I came to the error handling section I only retrieve INTERNAL.
Code I have tried:
exports.createCharge = functions.https.onCall((data, context) => {
var amount = data.amount * 100
var token = data.token
var info = {
amount: amount,
currency: 'usd',
description: 'Concession Charge',
source: token
}
stripe.charges.create(info, (err, charge)=>{
if(err){
throw new functions.https.HttpsError(err.type, err.message)
}else{
return charge
}
})
})
Since stripe.charges.create(info); returns a promise i've also tried:
return stripe.charges.create(info).then((charge) => {
return charge;
}).catch((error) =>{
throw new functions.https.HttpsError(err.type, err.message)
})
My client-side code is basically the same as the documentation.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//......
confirmBtn.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
submitPayment("tok_chargeDeclinedInsufficientFunds", 8.50)
.addOnCompleteListener(paymentCallback);
}
}
//......
}
//Function Call
private Task<String> submitPayment(String token, double amount) {
Log.d(TAG, "Submitting Payment of: $" + amount);
// Create the arguments to the callable function.
Map<String, Object> data = new HashMap<>();
data.put("token", token);
data.put("amount", amount);
return mFunctions
.getHttpsCallable("createCharge")
.call(data)
.continueWith(new Continuation<HttpsCallableResult, String>() {
#Override
public String then(#NonNull Task<HttpsCallableResult> task) throws Exception {
String result = (String) task.getResult().getData();
return result;
}
});
}
//Function Complete callback
OnCompleteListener<String> paymentCallback = new OnCompleteListener<String(){
#Override
public void onComplete(#NonNull Task<String> task){
if (!task.isSuccessful()){
Exception e = task.getException();
if (e instanceof FirebaseFunctionsException) {
FirebaseFunctionsException ffe = (FirebaseFunctionsException) e;
FirebaseFunctionsException.Code code = ffe.getCode();
Object details = ffe.getDetails(); //DEBUG BREAKPOINT HERE
}
}
}
}
};
I expect to have the Exception in my paymentCallback that reads the specific details of why it failed, but instead I only get an Internal error everytime.
According to the documentation:
To ensure the client gets useful error details, return errors from a callable by throwing (or returning a Promise rejected with) an instance of functions.https.HttpsError.
So I would try returning a rejected promise created with Promise.reject(...) rather than throwing an exception out of the catch callback.

Getting 404 in WebSocket connection

I have a backend java code for websocket.
SessionEndpoint:
#ServerEndpoint("/session")
public class SessionEndpoint {
private static Set<SessionEndpoint> sessionEndpoints = new CopyOnWriteArraySet<>();
#OnMessage
public void onMessage(Session session, String sessionId) {
Map<String, Object> attributes = new HashMap<>();
attributes.put("sessionId", sessionId);
sessionEndpoints.forEach(endpoint -> {
synchronized (endpoint) {
try {
session.getBasicRemote().sendObject(attributes);
} catch (IOException | EncodeException e) {
e.printStackTrace();
}
}
});
}
}
Trying to connect to websocket from javascript, code is given below.
let webSocket = new WebSocket('ws://localhost:9999/session');
webSocket.onopen = () => webSocket.send('hello');
webSocket.onmessage = function(response) {
console.log(response);
};
I get 404 response code while connecting to websocket. How should I connect to webscoket from javascript ?

Why is this Cloud Function called more than once with an Android HTTP request trigger?

I have a function in an Android app which sends a POST request to an HTTP triggered Cloud Function. Whenever I click the button once to send the message, Firebase registers the event twice on the Firebase console. My application is built in such a way that the button to send a message disappears after the first click, so I'm not accidentally double clicking the button, and when I step through the debugger, the function to send the POST request is only called once. Can you guys help me? I don't know much about Firebase and can't find good documentation or other questions like this.
Here's the method which sends a message to my FCM cloud function:
public void sendPushToSingleInstance(final Context activity, final String message, final String myId, final String theirId) {
final String url = URL_TO_CLOUD_FUNCTION;
StringRequest myReq = new StringRequest(Request.Method.POST, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Toast.makeText(activity, "Success", Toast.LENGTH_SHORT).show();
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
if (error.networkResponse != null)
Toast.makeText(activity, String.valueOf(error.networkResponse.statusCode), Toast.LENGTH_SHORT).show();
else
Toast.makeText(activity, "some error", Toast.LENGTH_SHORT).show();
}
}) {
#Override
public byte[] getBody() throws com.android.volley.AuthFailureError {
Map<String, String> rawParameters = new Hashtable<String, String>();
//not used
return new JSONObject(rawParameters).toString().getBytes();
};
public String getBodyContentType() {
return "application/json; charset=utf-8";
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("from", myId);
headers.put("message", message);
headers.put("sendingTo", theirId);
return headers;
}
};
Volley.newRequestQueue(activity).add(myReq);
}
My JavaScript takes the HTTP request, cuts it up and send the message to a topic which contains the other user's id (I did mean to do this verses sending to a specific device).
Here's the JavaScript for my Cloud Function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendMessage = functions.https.onRequest((request, response) => {
var topicId = request.get('sendingTo');
var color = request.get('color');
var from = request.get('from')
console.log('tried to push notification');
const payload = {
notification: {
title: from,
body: color,
sound: "default"
},
};
const options = {
priority: "high",
timeToLive: 60 * 60 * 24
};
admin.messaging().sendToTopic(topicId, payload, options);
});
Finally, here are the logs:
firebase console logs
Which say that the function was called twice.
I've tried many links for answers such as the standard,
https://firebase.google.com/docs/functions/http-events
and many StackOverflow posts. I haven't seen anyone else with the same problem.
From #mohamadrabee, "this from the documentation 'Always end an HTTP function with send(), redirect(), or end(). Otherwise, your function might to continue to run and be forcibly terminated by the system.' see firebase.google.com/docs/functions/http-events "
I added:
response.end();
after:
admin.messaging().sendToTopic(topicId, payload, options);
EDIT: After inserting this code, I still get the problem roughly 7% of the time. I had to change response.end(); to:
if (response.status(200)) {
response.status(200).end();
} else {
response.end();
}
I haven't had any problems since.

Categories

Resources