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, ...)
I am getting the Error "NoSuchMethodError: The method '[]' was called on null." from my stream. I tried to change my code several times and added print statements, which get printed correctly, but my Stream ends up returning an error, which is the one from the subject line. Any idea why? How can I Fix the error?
This is the result of the print data statement:
data:
{
userId1: 59jTMEbvqFd8C8UhInksauAVNk63,
userId2: 2ssfDEPhPhcIwInUWdlm0ReH5RZ2,
latestMessageTime: Timestamp(seconds=1667140814, nanoseconds=334000000),
lastMessageSenderId: 59jTMEbvqFd8C8UhInksauAVNk63,
created_at: 2022-10-26 19:44:13.793275,
latestMessage: TEST 3,
roomId: Qv30s8kATJbFJIWRdBEo
}
.
Stream<RoomsListModel> roomsStream() async* {
try {
// get all active chats
var rooms = await FirebaseFirestore.instance
.collection("rooms")
.where("users", arrayContains: userId)
.orderBy("latestMessageTime", descending: true)
.snapshots();
print("rooms: $rooms");
// get Other user details
await for (var room in rooms) {
for (var doc in room.docs) {
var data = doc.data() as Map<String, dynamic>;
print("data: $data");
var otherUser = await getOtherUser(
data["users"][0] == userId ? data["users"][1] : data["users"][0]);
print("otherUser: $otherUser");
yield RoomsListModel(
roomId: doc.id,
userId: otherUser["user id"],
avatar: otherUser["photoUrl"],
name: otherUser["name"],
lastMessage: data["latestMessage"],
lastMessageTime: data["latestMessageTime"]);
}
}
} catch (e) {
print("Error: $e");
}
}
.
Future getOtherUser(String id) async {
// get other user profile
var user = await FirebaseFirestore.instance
.collection("users")
.doc(id)
.get()
.then((value) => value.data()) as Map<String, dynamic>;
// return other user profile
return user;
}
change this:
var otherUser = await getOtherUser(
data["users"][0] == userId ? data["users"][1] : data["users"][0]);
to this:
var otherUser = await getOtherUser(
data["userId1"] == userId ? data["userId2"] : data["userId1"]);
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
so I have callable cloud function like this
exports.callableModeratorSendNotification = functions.https.onCall(async (data, context) => {
try {
const platform = data.platform
const priority = data.priority
const message = data.message
const moderatorID = context.auth.uid
// check if moderator have authorization to send notification and check if moderator is active or not
const moderatorSnapshot = await db.doc(`moderators/${moderatorID}`).get()
const moderator = moderatorSnapshot.data()
if ( !moderator.canSendNotification || !moderator.isActive) {
throw new functions.https.HttpsError('invalid-moderator', 'Moderator is invalid. only active moderator who has authorization that can verify a user.')
}
let query
const now = new Date()
const xMonthsAgo = moment().subtract(1,"months").toDate()
query = db.collection(`devices`)
.where("lastActivity","<=",now)
.where("lastActivity",">=",xMonthsAgo)
if ( platform !== "All") {
query = query.where("platform","==",platform) // iOS or Android
}
const devicesQuerySnapshot = await query.get()
devicesQuerySnapshot.docs.forEach( deviceSnapshot => {
const device = deviceSnapshot.data()
console.log(device.userID)
})
{result: 'success'}
} catch(error) {
console.log(error)
return null
}
})
at the moment, that function is just to log the user ID. and that function will return 200 code like this in console
but the problem is in the client side.... I am using Android to call that function, like this
fun sendNotification(platform: String, priority: String, message: String) : Task<String> {
// Create the arguments to the callable function.
val data = hashMapOf(
"platform" to platform,
"priority" to priority,
"message" to message
)
return functions
.getHttpsCallable("callableModeratorSendNotification")
.call(data)
.continueWith { task ->
val result = task.result?.data as String
result
}
}
and then use it like this
sendNotification(selectedPlatform,selectedPriority,message).addOnSuccessListener {
toast("successful")
}.addOnFailureListener {
toast("failed")
}
the problem is, even though the status code in the console is 200, but addOnFailureListener will always be called. I expect addOnSuccessListener will be called if the response is 200
here is the exception caught by the failure listener
java.util.HashMap cannot be cast to java.lang.String 2020-02-04
11:30:09.023 26024-26024/com.
java.lang.ClassCastException: java.util.HashMap cannot be cast to
java.lang.String
in this line ?
val result = task.result?.data as String
where does the result come from. does it represent the return on my callable function ? I assume callable function should return a promise like cloud function trigger
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
});
});