I am sending json to cloud function via postman and it works fine, how can I send same json as map to cloud function:
Sending via postman:
{
"data": {
"users": [
{
"phone": "55512345"
},
{
"phone": "972525276676"
},
{
"phone": "55512347"
}
]
}
}
Sending via android:
private fun addMessage(): Task<String>? {
val usr1 = User("55512345")
val usr2 = User("972525276676")
val usr3 = User("55512347")
val userList = listOf(usr1,usr2,usr3)
val data: MutableMap<String, Any> = HashMap()
data["users"] = userList
functions.getHttpsCallable("getUsers")
.call(data)
.addOnFailureListener {
Log.d("DTAG", it.toString())
}
.addOnSuccessListener {
Log.d("DTAG","Ok: ${it.data.toString()}")
}
return null
}
Where user is:
data class User(var phone:String)
Cloud function:
exports.getUsers = functions.https.onRequest(async (request, response) => {
const data = request.body.data;
if (data !== null && data.users !== null) {
const users = data.users;
const phonelist = users.map(user => user.phone.toString());
const userlist = []
const snapshot = await db.collection("users").get()
snapshot.docs.forEach((userDoc) => {
const phone = userDoc.get("phone")
if(phone === null) return;
const isContain = phonelist.reduce((acc, num) => acc || phone.includes(num), false)
if(isContain) {
userlist.push(userDoc.data())
}
})
response.status(200).json({result: userlist})
} else{
response.sendStatus(403)
}
});
Error:
Object cannot be encoded in JSON: User(phone=55512345)
The error is telling you that the client SDK doesn't know what to do with the User class. You'll have to build a Map<String, Object> out of the data.
val userMap1 = mapOf("phone" to "55512345")
...
val userList = listOf(userMap1, ...)
Related
Am trying to add Stripe to my android app with Firebase. I set the publishable key in gradle.properties and builtype in gradle.build then call in application as BuildConfig.PublishableKey
Every time I try to add card a dialog pops up with this warning...
unexpected char 0x0a at 9 header value: bearer pk_test_xxxxkeyxxxx
Any ideas? Am using prebuilt UI from stripe too (presentPaymentMethodSelection() ) following their Firebase mobile payments android
var RC_SIGN_IN = 1
class MainActivityStripe : AppCompatActivity() {
private var currentUser: FirebaseUser? = null
private lateinit var paymentSession: PaymentSession
private lateinit var selectedPaymentMethod: PaymentMethod
private val stripe: Stripe by lazy { Stripe(applicationContext,
PaymentConfiguration.getInstance(applicationContext).publishableKey) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_stripe)
currentUser = FirebaseAuth.getInstance().currentUser
payButton.isEnabled = false
loginButton.setOnClickListener {
// login to firebase
val providers = arrayListOf(
AuthUI.IdpConfig.EmailBuilder().build())
startActivityForResult(
AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(providers)
.build(),
RC_SIGN_IN
)
}
payButton.setOnClickListener {
confirmPayment(selectedPaymentMethod.id!!)
}
paymentmethod.setOnClickListener {
Toast.makeText(applicationContext, PaymentConfiguration.getInstance(applicationContext).publishableKey, Toast.LENGTH_LONG).show()
// Create the customer session and kick start the payment flow
paymentSession.presentPaymentMethodSelection()
}
showUI()
}
private fun confirmPayment(paymentMethodId: String) {
val paymentCollection = Firebase.firestore
.collection("stripe_customers").document(currentUser?.uid?:"")
.collection("payments")
// Add a new document with a generated ID
paymentCollection.add(hashMapOf(
"amount" to 8800,
"currency" to "cad"
))
.addOnSuccessListener { documentReference ->
Log.d("paymentsss", "DocumentSnapshot added with ID: ${documentReference.id}")
documentReference.addSnapshotListener { snapshot, e ->
if (e != null) {
Log.w("paymentsss", "Listen failed.", e)
return#addSnapshotListener
}
if (snapshot != null && snapshot.exists()) {
Log.d("paymentsss", "Current data: ${snapshot.data}")
val clientSecret = snapshot.data?.get("client_secret")
Log.d("paymentsss", "Create paymentIntent returns $clientSecret")
clientSecret?.let {
stripe.confirmPayment(this, ConfirmPaymentIntentParams.createWithPaymentMethodId(
paymentMethodId,
(it as String)
))
checkoutSummary.text = "Thank you for your payment"
Toast.makeText(applicationContext, "Payment Done!!", Toast.LENGTH_LONG).show()
}
} else {
Log.e("paymentsss", "Current payment intent : null")
payButton.isEnabled = true
}
}
}
.addOnFailureListener { e ->
Log.w("paymentsss", "Error adding document", e)
payButton.isEnabled = true
}
}
private fun showUI() {
currentUser?.let {
loginButton.visibility = View.INVISIBLE
greeting.visibility = View.VISIBLE
checkoutSummary.visibility = View.VISIBLE
payButton.visibility = View.VISIBLE
paymentmethod.visibility = View.VISIBLE
greeting.text = "Hello ${it.displayName}"
setupPaymentSession()
}?: run {
// User does not login
loginButton.visibility = View.VISIBLE
greeting.visibility = View.INVISIBLE
checkoutSummary.visibility = View.INVISIBLE
paymentmethod.visibility = View.INVISIBLE
payButton.visibility = View.INVISIBLE
payButton.isEnabled = false
}
}
private fun setupPaymentSession () {
// Setup Customer Session
CustomerSession.initCustomerSession(this, FirebaseEphemeralKeyProvider())
// Setup a payment session
paymentSession = PaymentSession(this, PaymentSessionConfig.Builder()
.setShippingInfoRequired(false)
.setShippingMethodsRequired(false)
.setBillingAddressFields(BillingAddressFields.None)
.setShouldShowGooglePay(true)
.build())
paymentSession.init(
object: PaymentSession.PaymentSessionListener {
override fun onPaymentSessionDataChanged(data: PaymentSessionData) {
Log.d("PaymentSession1", "11PaymentSession has changed: $data")
Log.d("PaymentSession11", "1111 ${data.isPaymentReadyToCharge} <> ${data.paymentMethod}")
if (data.isPaymentReadyToCharge) {
Log.d("PaymentSession2", "222Ready to charge");
payButton.isEnabled = true
data.paymentMethod?.let {
Log.d("PaymentSession3", "333PaymentMethod $it selected")
paymentmethod.text = "${it.card?.brand} card ends with ${it.card?.last4}"
selectedPaymentMethod = it
}
}
}
override fun onCommunicatingStateChanged(isCommunicating: Boolean) {
Log.d("PaymentSession4", "444isCommunicating $isCommunicating")
}
override fun onError(errorCode: Int, errorMessage: String) {
Log.e("PaymentSession5", "555onError: $errorCode, $errorMessage")
}
}
)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == RC_SIGN_IN) {
val response = IdpResponse.fromResultIntent(data)
if (resultCode == Activity.RESULT_OK) {
currentUser = FirebaseAuth.getInstance().currentUser
Log.d("Login", "User ${currentUser?.displayName} has signed in.")
Toast.makeText(applicationContext, "Welcome ${currentUser?.displayName}", Toast.LENGTH_SHORT).show()
showUI()
} else {
Log.d("Login", "Signing in failed!")
Toast.makeText(applicationContext, response?.error?.message?:"Sign in failed", Toast.LENGTH_LONG).show()
}
} else {
paymentSession.handlePaymentData(requestCode, resultCode, data ?: Intent())
}
}
}**
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const { Logging } = require('#google-cloud/logging');
const logging = new Logging({
projectId: process.env.GCLOUD_PROJECT,
});
admin.initializeApp();
const stripe = require('stripe')(functions.config().stripe.secret, {
apiVersion: '2020-03-02',
});
exports.createEphemeralKey = functions.https.onCall(async (data, context) => {
// Checking that the user is authenticated.
if (!context.auth) {
// Throwing an HttpsError so that the client gets the error details.
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called while authenticated!'
);
}
const uid = context.auth.uid;
try {
if (!uid) throw new Error('Not authenticated!');
// Get stripe customer id
const customer = (
await admin.firestore().collection('stripe_customers').doc(uid).get()
).data().customer_id;
const key = await stripe.ephemeralKeys.create(
{ customer: customer },
{ apiVersion: data.api_version }
);
return key;
} catch (error) {
throw new functions.https.HttpsError('internal', error.message);
}
});
exports.createStripeCustomer = functions.auth.user().onCreate(async (user) => {
const customer = await stripe.customers.create({
email: user.email,
metadata: { firebaseUID: user.uid },
});
await admin.firestore().collection('stripe_customers').doc(user.uid).set({
customer_id: customer.id,
});
return;
});
exports.createStripePayment = functions.firestore
.document('stripe_customers/{userId}/payments/{pushId}')
.onCreate(async (snap, context) => {
const { amount, currency } = snap.data();
try {
// Look up the Stripe customer id.
const customer = (await snap.ref.parent.parent.get()).data().customer_id;
// Create a charge using the pushId as the idempotency key
// to protect against double charges.
const idempotencyKey = context.params.pushId;
const payment = await stripe.paymentIntents.create(
{
amount,
currency,
customer,
},
{ idempotencyKey }
);
// If the result is successful, write it back to the database.
await snap.ref.set(payment);
} catch (error) {
// We want to capture errors and render them in a user-friendly way, while
// still logging an exception with StackDriver
console.log(error);
await snap.ref.set({ error: userFacingMessage(error) }, { merge: true });
await reportError(error, { user: context.params.userId });
}
});
const updatePaymentRecord = async (id) => {
// Retrieve the payment object to make sure we have an up to date status.
const payment = await stripe.paymentIntents.retrieve(id);
const customerId = payment.customer;
// Get customer's doc in Firestore.
const customersSnap = await admin
.firestore()
.collection('stripe_customers')
.where('customer_id', '==', customerId)
.get();
if (customersSnap.size !== 1) throw new Error('User not found!');
// Update record in Firestore
const paymentsSnap = await customersSnap.docs[0].ref
.collection('payments')
.where('id', '==', payment.id)
.get();
if (paymentsSnap.size !== 1) throw new Error('Payment not found!');
await paymentsSnap.docs[0].ref.set(payment);
};
exports.cleanupUser = functions.auth.user().onDelete(async (user) => {
const dbRef = admin.firestore().collection('stripe_customers');
const customer = (await dbRef.doc(user.uid).get()).data();
await stripe.customers.del(customer.customer_id);
// Delete the customers payments & payment methods in firestore.
const snapshot = await dbRef
.doc(user.uid)
.collection('payment_methods')
.get();
snapshot.forEach((snap) => snap.ref.delete());
await dbRef.doc(user.uid).delete();
return;
});
exports.handleWebhookEvents = functions.https.onRequest(async (req, resp) => {
const relevantEvents = new Set([
'payment_intent.succeeded',
'payment_intent.processing',
'payment_intent.payment_failed',
'payment_intent.canceled',
]);
let event;
try {
event = stripe.webhooks.constructEvent(
req.rawBody,
req.headers['stripe-signature'],
functions.config().stripe.webhooksecret
);
} catch (error) {
console.error('❗️ Webhook Error: Invalid Secret');
resp.status(401).send('Webhook Error: Invalid Secret');
return;
}
if (relevantEvents.has(event.type)) {
try {
switch (event.type) {
case 'payment_intent.succeeded':
case 'payment_intent.processing':
case 'payment_intent.payment_failed':
case 'payment_intent.canceled':{
const id = event.data.object.id;
await updatePaymentRecord(id);
break;
}
default:
throw new Error('Unhandled relevant event!');
}
} catch (error) {
console.error(
`❗️ Webhook error for [${event.data.object.id}]`,
error.message
);
resp.status(400).send('Webhook handler failed. View Function logs.');
return;
}
}
// Return a response to Stripe to acknowledge receipt of the event.
resp.json({ received: true });
});
function reportError(err, context = {}) {
// This is the name of the StackDriver log stream that will receive the log
// entry. This name can be any valid log stream name, but must contain "err"
// in order for the error to be picked up by StackDriver Error Reporting.
const logName = 'errors';
const log = logging.log(logName);
// https://cloud.google.com/logging/docs/api/ref_v2beta1/rest/v2beta1/MonitoredResource
const metadata = {
resource: {
type: 'cloud_function',
labels: { function_name: process.env.FUNCTION_NAME },
},
};
// https://cloud.google.com/error-reporting/reference/rest/v1beta1/ErrorEvent
const errorEvent = {
message: err.stack,
serviceContext: {
service: process.env.FUNCTION_NAME,
resourceType: 'cloud_function',
},
context: context,
};
// Write the error log entry
return new Promise((resolve, reject) => {
log.write(log.entry(metadata, errorEvent), (error) => {
if (error) {
return reject(error);
}
return resolve();
});
});
}
// [END reporterror]
/**
* Sanitize the error message for the user.
*/
function userFacingMessage(error) {
return error.type
? error.message
: 'An error occurred, developers have been alerted';
}
buildTypes.each {
it.buildConfigField 'String', 'PublishableKey', stripePublishableKey
}
import android.util.Log
import com.google.firebase.functions.FirebaseFunctionsException
import com.google.firebase.functions.ktx.functions
import com.google.firebase.ktx.Firebase
import com.stripe.android.EphemeralKeyProvider
import com.stripe.android.EphemeralKeyUpdateListener
class FirebaseEphemeralKeyProvider: EphemeralKeyProvider {
override fun createEphemeralKey(
apiVersion: String,
keyUpdateListener: EphemeralKeyUpdateListener
) {
val data = hashMapOf(
"api_version" to apiVersion
)
// User firebase to call the functions
Firebase.functions
.getHttpsCallable("createEphemeralKey")
.call(data)
.continueWith { task ->
if (!task.isSuccessful) {
val e = task.exception
if (e is FirebaseFunctionsException) {
val code = e.code
val message = e.message
Log.e("EphemeralKey", "Ephemeral key provider returns error: $e $code $message")
}
}
val key = task.result?.data.toString()
Log.d("EphemeralKey", "Ephemeral key provider returns $key")
keyUpdateListener.onKeyUpdate(key)
}
}
}
import android.app.Application
import com.stripe.android.PaymentConfiguration
class MyApp : Application(){
override fun onCreate() {
super.onCreate()
PaymentConfiguration.init(applicationContext, BuildConfig.PublishableKey)
}
}```
>2020-09-07 09:23:29.753 15967-15967/com.asgd.indigenoussource D/PaymentSession4: 444isCommunicating true
>2020-09-07 09:23:29.754 15967-15967/com.asgd.indigenoussource D/PaymentSession1: 11PaymentSession has changed: PaymentSessionData(isShippingInfoRequired=false, isShippingMethodRequired=false, cartTotal=0, shippingTotal=0, shippingInformation=null, shippingMethod=null, paymentMethod=null, useGooglePay=false)
>2020-09-07 09:23:29.754 15967-15967/com.asgd.indigenoussource D/PaymentSession11: 1111 false <> null
>2020-09-07 09:23:31.636 15967-15967/com.asgd.indigenoussource D/PaymentSession4: 444isCommunicating false
After getting some help from someone with stripe was able to get it working. My publishable key some how got changed within gradle and that key gets linked with device. I had to make new emulated device and reset everything
I am trying to send notifications to an Android app's users with firebase cloud messaging. I am using cloud firestore triggers but when trying to access a user node's properties, they are undefined.
Here is my index.js :
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.notifyNewMessage = functions.firestore
.document('conversations/{conversation}/messages/{message}')
.onCreate((docSnapshot, context) => {
const message = docSnapshot.data();
const recipientId = message['recipientId'];
const senderName = message['senderName'];
return admin.firestore().collection("users").doc(recipientId).get().then(userDoc => {
const registrationTokens = userDoc.registrationTokens;
console.log("registrationTokens = "+ registrationTokens);
const notificationBody = message['message'];
const payload = {
notification : {
title : senderName + " sent you a message,",
body: notificationBody,
clickAction: "ConversationActivity"
},
data : {
contactName : senderName,
userId : message['senderId']
}
}
return admin.messaging().sendToDevice(registrationTokens, payload).then(response => {
const stillRegisteredTokens = registrationTokens;
response.results.forEach((result, index) => {
const error = result.error;
if (error){
const failedRegistrationToken = stillRegisteredTokens['index'];
if (error.code === 'messaging/invalid-registration-token'
|| error.code === 'messaging/registration-token-not-registered') {
const failedIndex = stillRegisteredTokens.indexOf(failedRegistrationToken)
if (failedIndex > -1) {
stillRegisteredTokens.splice(failedIndex, 1);
}
}
}
})
return admin.firestore().doc("users/" + recipientId).update({
registrationTokens: stillRegisteredTokens
})
})
})
})
Because of that I get an error "sendDevice() argument must be non-empty array or non null string"
UPDATE
registrationTokens were undefined because I called userDoc instead of userDoc.data()
Now registrationTokens is not null nor empty but I still get the error :
Registration token(s) provided to sendToDevice() must be a non-empty string or a non-empty array.
I am trying to pass a custom object which is of type User from native platform to Flutter. The User class is part of a library and not accessible directly for editing. Here is my android and iOS code implementation for the same. Problem is I am not able to find a solution on how to pass this object through method channels in such a way that I can parse it in the Dart code easily.
Android part:
private fun loginUser(uid: String, apiKey: String, result: MethodChannel.Result) {
MyChat.login(uid, apiKey, object : MyChat.CallbackListener<User>() {
override fun onSuccess(user: User) {
Log.e(TAG, user.toString())
result.success(hashMapOf("RESULT" to true, "AVATAR" to user.avatar,
"CREDITS" to user.credits,
"EMAIL" to user.email,
"LAST_ACTIVE" to user.lastActiveAt,
"NAME" to user.name,
"ROLE" to user.role,
"STATUS" to user.status,
"STATUS_MESSAGE" to user.statusMessage).toString())
}
override fun onError(p0: MyChatException?) {
Log.e(TAG, p0?.message)
result.error("FAILED", "Unable to create login", null)
}
})
}
iOS implementation:
func loginUser(result: #escaping FlutterResult, uid: String, apiKey: String){
MyChat.login(UID: uid, apiKey: apiKey, onSuccess: { (user) in
// Login Successful
let data: [String: Any] = ["RESULT":true,
"AVATAR":user.avatar!,
"CREDITS": user.credits,
"EMAIL": user.email!,
"LAST_ACTIVE":String(user.lastActiveAt),
"NAME":user.name!,
"ROLE":user.role!,
"STATUS":user.status.rawValue,
"STATUS_MESSAGE":user.statusMessage]
let jsonData = try? JSONSerialization.data(withJSONObject: data, options: [.prettyPrinted])
result(String(data: jsonData!, encoding: .ascii))
}) { (error) in
// Login error
result(FlutterError(code: "FAILED", message:"Login failed with exception: " + error.errorDescription, details: nil))
}
}
My dart code:
Future<String> isUserLoggedIn() async {
String status = "";
try {
final String result = await platform
.invokeMethod('loginUser', {"UID": UID, "API_KEY": API_KEY});
print(result); //How to parse?
status = "Hello";
} on PlatformException catch (e) {
print("Exception");
status = e.message;
}
return status;
}
You can pass data in hash map.
In Android:
result.success(hashMapOf(
"CREDITS" to user.credits,
"EMAIL" to user.email,
...
))
In iOS:
let data: [String: Any] = [...]
result(data)
In Flutter:
final result = await platform.invokeMethod<Map<String, dynamic>>('loginUser', ...);
final credits = result['CREDITS'] as String;
final email = result['EMAIL'] as String;
...
you can use invokeMapMethod which is an implementation of invokeMethod that can return typed maps.
like this :
final result = await platform.invokeMapMethod('loginUser', ...);
or you can pass json object as string like that :
in android
platform.success(
"{\"CREDITS\":\"${user.credits}\",\"EMAIL\":\"${user.email}\",\"LAST_ACTIVE\":\"${user.lastActiveAt}\"}"
)
in flutter
var result = await methodChannel.invokeMethod('loginUser' , '');
var json = json.decode(result);
var credit = json['CREDITS'];
var email = json['EMAIL'];
var lastActive = json['LAST_ACTIVE'];
I am sending push notification from iOS and Android both by calling a cloud function, and in each device I am getting 5 times a single push notification. I am using parse database hosted on back4app.
Cloud code is given below:
Parse.Cloud.define("push", function (request, response) {
var query = new Parse.Query(Parse.Installation);
var userID = request.params.user;
var message = request.params.message;
var notificationType = request.params.notificationType;
var user = new Parse.User();
user.id = userID;
query.equalTo('user', user);
query.equalTo("allowPush", true);
Parse.Push.send({
where: query,
data: {
alert: message,
sound: 'default',
"type": notificationType
}
}, { useMasterKey: true });
});
Try to call reponse.success and response.error functions in the end of your cloud code function. Since your client code is not receiving the feedback if the call worked or not it is probably attempting to send again.
Parse.Cloud.define("push", function (request, response) {
var query = new Parse.Query(Parse.Installation);
var userID = request.params.user;
var message = request.params.message;
var notificationType = request.params.notificationType;
var user = new Parse.User();
user.id = userID;
query.equalTo('user', user);
query.equalTo("allowPush", true);
Parse.Push.send({
where: query,
data: {
alert: message,
sound: 'default',
"type": notificationType
}
},
{
success: function () { response.success(); },
error: function(err) { response.error(err); },
useMasterKey: true
});
});
I am new to Nativescript and trying to build an Android App.I am sending a POST request and getting an proper response.But the problem is I can not bind the response data to Observable.I am trying to bind the data[0].value to price in my Viewmodel.But I have failed to do that.But if I try to set hardcoded data like this Viewmodel.set("price","1000$") then it's ok.But i can not set response data.
var fetchModule = require("fetch");
var Observable = require("data/observable").Observable;
var ObservableArray = require("data/observable-array").ObservableArray;
var data;
var person;
var priceFromResponse;
function ModelViewModel() {
var viewmodel = new Observable();
var Viewmodel =new Observable();
viewmodel.get=function(model){
data = model;
}
var viewModel = new ObservableArray();
viewModel.load = function() {
fetch("http://10.0.2.2:8000/get_model", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
brand: data,
})
}).then(r => { return r.json(); }).then(function (data) {
for(var i=0;i<data.length;i++){
viewModel.push({
model: data[i].product,
});
};
priceFromResponse =data[0].value;
}).then(function(price){
})
}
viewModel.empty = function() {
while (viewModel.length) {
viewModel.pop();
}
};
**Viewmodel.set("price",priceFromResponse);**
return [viewModel,viewmodel,Viewmodel];
}
module.exports = ModelViewModel;