Google Play Billing v5 - Get a product's price - in-app-billing

In v4, I used to have SkuDetails.price to get the price of the product but now it's not available anymore in the new ProductDetails in v5.
How can I get the price of a product in this new version?

When you call getSubscriptionOfferDetails, it returns the offers available to buy for the subscription product. Then you can call getPricingPhases() to get the list of the pricing phases. Each pricing phase object has a getFormattedPrice() call to get the price of an offer's pricing phrase (https://developer.android.com/reference/com/android/billingclient/api/ProductDetails.PricingPhase)

You have to check for Available Products
fun getAvailableProducts() {
Timber.d("!!! Getting available products to buy ...")
val queryProductDetailsParams =
QueryProductDetailsParams.newBuilder()
.setProductList(
listOf(
QueryProductDetailsParams.Product.newBuilder()
.setProductId(SKU_SUBSCRIBE_MONTHLY)
.setProductType(BillingClient.ProductType.SUBS)
.build(),
QueryProductDetailsParams.Product.newBuilder()
.setProductId(SKU_SUBSCRIBE_YEARLY)
.setProductType(BillingClient.ProductType.SUBS)
.build()
))
.build()
billingClient.queryProductDetailsAsync(queryProductDetailsParams) {
billingResult,
productDetailsList ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
availableProducts.tryEmit(productDetailsList)
getPrices(productDetailsList)
} else {
Timber.d("!!!Error getting available Products to buy: ${billingResult.responseCode} ${billingResult.debugMessage}")
}
}
}
And then
private fun getPrices(productDetailsList: MutableList<ProductDetails>) {
productDetailsList.forEach{
when (it.productId) {
SKU_SUBSCRIBE_MONTHLY -> {
currency.tryEmit(it.subscriptionOfferDetails?.get(0)?.pricingPhases!!.pricingPhaseList[0]?.priceCurrencyCode.toString())
monthlyPrice.tryEmit(it.subscriptionOfferDetails?.get(0)?.pricingPhases!!.pricingPhaseList[0]?.formattedPrice.toString())
Timber.d("!!!! $it.")
}
SKU_SUBSCRIBE_YEARLY -> {
// currency.tryEmit(it.subscriptionOfferDetails?.get(0)?.pricingPhases!!.pricingPhaseList[0]?.priceCurrencyCode.toString())
yearlyPrice.tryEmit(it.subscriptionOfferDetails?.get(0)?.pricingPhases!!.pricingPhaseList[0]?.formattedPrice.toString())
Timber.d("!!!! $it.")
}
}
}
}

i use the following code to get price details.
private void queryProduct() {
QueryProductDetailsParams queryProductDetailsParams
= QueryProductDetailsParams.newBuilder().setProductList(
ImmutableList.of(QueryProductDetailsParams.Product.newBuilder()
.setProductId("your_product_id")
.setProductType(BillingClient.ProductType.INAPP).build()))
.build();
billingClient.queryProductDetailsAsync(
queryProductDetailsParams,
new ProductDetailsResponseListener() {
#Override
public void onProductDetailsResponse(#NonNull BillingResult billingResult, #NonNull List<ProductDetails> list) {
if(!list.isEmpty()){
productDetails = list.get(0);
itemdesc.setText(productDetails.getName());
itemprice.setText(productDetails.getOneTimePurchaseOfferDetails().getFormattedPrice());
itemprice.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
makePurchase();
}
});
}else {
Log.i("playsresponse", "no response from google play");
}
}
}
);

Related

How to Gender Filtering with Photon?

I'm developing a multiplayer game with unity. I am using playfab and photon plugin in the game. Players choose gender when logging in and I save this selection to player data in playfab. Also, I am making a simple match system with photon. (The player presses the "match" button, joins a room randomly, if there is no room to join, player sets up a room for 2 people and waits for the other player, when the number of players joining the room is 2, they switch to the room screen.)
Here is the code file where I make the players choose their gender:
public void choosemale()
{
eseç.SetActive(true);
kseç.SetActive(false);
}
public void choosefemale()
{
eseç.SetActive(false);
kseç.SetActive(true);
}
public void SetUserDataMale()
{
PlayFabClientAPI.UpdateUserData(new UpdateUserDataRequest()
{
Data = new Dictionary<string, string>() {
{"Gender", "Male"},
},
Permission = UserDataPermission.Public
},
result => Debug.Log("Successfully updated user data"),
error => {
Debug.Log("Got error setting user data Ancestor to Male");
Debug.Log(error.GenerateErrorReport());
});
}
public void SetUserDataFemale()
{
PlayFabClientAPI.UpdateUserData(new UpdateUserDataRequest()
{
Data = new Dictionary<string, string>() {
{"Gender", "Female"},
},
Permission = UserDataPermission.Public
},
result => Debug.Log("Successfully updated user data"),
error => {
Debug.Log("Got error setting user data Ancestor to Female");
Debug.Log(error.GenerateErrorReport());
}
) ;
} void OnLoginSuccess(LoginResult result)
{
if (eseç.activeSelf == true)
SetUserDataMale();
if (kseç.activeSelf == true)
SetUserDataFemale();
}
The code file I used for the matching:
public void FindMatch()
{
PhotonNetwork.JoinRandomRoom();
Debug.Log("Eşleşme Aranıyor");
}
public void StopSearch()
{
PhotonNetwork.LeaveRoom();
}
public override void OnJoinRandomFailed(short returnCode, string message)
{
Debug.Log("Fail");
MakeRoom();
}
void MakeRoom()
{
RoomOptions roomOptions = new RoomOptions()
{
IsVisible = true,
IsOpen = true,
MaxPlayers = 2
};
PhotonNetwork.CreateRoom("Room", roomOptions, EslesLobby);
Debug.Log("oda oluşturuldu, diğer oyuncu bekleniyor");
}
public override void OnJoinedRoom()
{
if (PhotonNetwork.CurrentRoom.PlayerCount == 2 && PhotonNetwork.IsMasterClient)
{
Debug.Log(PhotonNetwork.CurrentRoom.PlayerCount + "/2 Start Watch");
onlinep.SetActive(true);
_roomsCanvases.CurrentRoomCanvas.Show();
}
}
public override void OnCreatedRoom()
{
Debug.Log("OdaKurma Başarılı");
}
public override void OnPlayerEnteredRoom(Player newPlayer)
{
if(PhotonNetwork.CurrentRoom.PlayerCount ==2 && PhotonNetwork.IsMasterClient)
{
Debug.Log(PhotonNetwork.CurrentRoom.PlayerCount+"/2 Start Watch");
onlinep.SetActive(true);
_roomsCanvases.CurrentRoomCanvas.Show();
}
}
What I want to ask is this: I have a simple filtering bar. In this bar, when the player presses the male button, I want player to randomly join one of the rooms with only male players.How can I do that. I would be very happy if you help me.
Initially I find out the gender of the player using this code:
void GetUserData(string myPlayFabeId)
{
PlayFabClientAPI.GetUserData(new GetUserDataRequest()
{
PlayFabId = myPlayFabeId,
Keys = null
}, result => {
Debug.Log("Got user data:");
if (result.Data == null || !result.Data.ContainsKey("Gender")) Debug.Log("No Gender");
else Debug.Log("Ancestor: " + result.Data["Gender"].Value);
}, (error) => {
Debug.Log("Got error retrieving user data:");
Debug.Log(error.GenerateErrorReport());
});
}
You could simply split rooms into separate lists by using Photon's lobbies.

What is friendUserId in Snapkit?

I'm connecting my app with bitmoji here, now i wanted to add Friendmoji also but In official documentation "friendUserId: the external ID of the friend user provided by the app" is mentioned but from where we get this external id is not properly specified! so what do i set for the friendUserId?
You need to load external id when userIsLoggedIn. Like this way:
if (SnapLogin.isUserLoggedIn(this)) {
loadExternalId();
}
private void loadExternalId() {
SnapLogin.fetchUserData(this, EXTERNAL_ID_QUERY, null, new FetchUserDataCallback() {
#Override
public void onSuccess(#Nullable UserDataResponse userDataResponse) {
if (userDataResponse == null || userDataResponse.hasError()) {
return;
}
mMyExternalId = userDataResponse.getData().getMe().getExternalId();
mFriendmojiToggle.setVisibility(View.VISIBLE);
}
#Override
public void onFailure(boolean isNetworkError, int statusCode) {
// handle error
}
});
}

Californium CoAP path parameters

I'm working on a CoAP application using Eclipse Californium and I need to pass parameters using the URL as we do in restful web services. Is it possible to do it in californium coap implementation, and if so please let me know how to do it.
ex:
coap://localhost:5683/foo/{fooID}
The short answer is yes you can do it.
As stated in JavaDocs
When a request arrives at the server, the {#link
ServerMessageDeliverer} searches in the resource tree for the
destination resource. It travels down the resource tree by looking
for one element of the destination URI after another and by calling
the method {#link #getChild(String)} on each element. It is allowed
to override this method and to return an arbitrary resource. This
allows for instance to serve URIs with wildcards or delegate requests
to any sub-URI to the same resource.
So basically you have to override deliverRequest and maybe findResource methods in org.eclipse.californium.core.server.ServerMessageDeliverer in order to return appropriate resource which will handle the request. Also it will be required to analyse Exchange Request UriPath as a part of resource handleGET/PUT/POST/etc to fetch path variable (this can be done by using CoapExchange.advanced().getRequest().getOptions().getUriPath())
Based on the source code of Californium it should be pretty easy to override the default behaviour of a request deliverer.
Good luck with that!
From what I have seen so far, creating a custom ServerMessageDeliverer seems to be the more complicated solution. Actually it looks like the correct solution is to override CoapResource#getChild(String) so it returns the resource you want to be associated with that name. The ServerMessageDeliverer looks to me more like the way to implement some sort of controller that delivers or distribute requests in a more complicated environment.
For the question where the last part of the URI is the parameter, the solution could look like this:
public class UriParameterResource extends CoapResource {
public UriParameterResource() {
super("foo");
}
#Override
public void handleGET(CoapExchange exchange) {
List<String> uriPath = exchange.getRequestOptions().getUriPath();
// check if there is a sub-resource given, and if so use it for processing
if (uriPath.size() > 1) {
exchange.respond("Process " + uriPath.get(1));
} else {
exchange.respond(ResponseCode.NOT_IMPLEMENTED);
}
}
#Override
public Resource getChild(String name) {
// even sub-resources will land in the GET of this resource
return this;
}
}
Regarding the the answer from #Copernic, I personally think it does not match the idea of REST. Each part of the URI path should return its own resource related to its parent, which makes it a tree structure per definition and not a flat list that simply inspects parts of the path as some sort of parameter.
IMHO even the sensors example could be resolved by using CoapResource implementations where the variable child resource could be dynamically resolved. The snippet below is just an example, of course this would need to be dependent on the real situation where a house and its rooms would need to register somehow.
public class HousesResource extends CoapResource {
public HousesResource() {
super("houses");
}
#Override
public void handleGET(CoapExchange exchange) {
// could return a list of available houses
exchange.respond(ResponseCode.NOT_IMPLEMENTED);
}
#Override
public Resource getChild(String name) {
Resource child = super.getChild(name);
if (child == null) {
child = new HouseResource(name);
add(child);
}
return child;
}
class HouseResource extends CoapResource {
public HouseResource(String name) {
super(name);
add(new RoomsResource());
}
#Override
public void handleGET(CoapExchange exchange) {
exchange.respond(ResponseCode.NOT_IMPLEMENTED);
}
}
class RoomsResource extends CoapResource {
public RoomsResource() {
super("rooms");
}
#Override
public void handleGET(CoapExchange exchange) {
// could return a list of available rooms
exchange.respond(ResponseCode.NOT_IMPLEMENTED);
}
#Override
public Resource getChild(String name) {
Resource child = super.getChild(name);
if (child == null) {
child = new RoomResource(name);
add(child);
}
return child;
}
}
class RoomResource extends CoapResource {
public RoomResource(String roomName) {
super(roomName);
add(new SensorsResource());
}
#Override
public void handleGET(CoapExchange exchange) {
// could return a summary board about the room
exchange.respond(ResponseCode.NOT_IMPLEMENTED);
}
}
class SensorsResource extends CoapResource {
public SensorsResource() {
super("sensors");
add(new TemperatureResource());
}
#Override
public void handleGET(CoapExchange exchange) {
// this could return a list of registered sensors
exchange.respond(ResponseCode.NOT_IMPLEMENTED);
}
}
class TemperatureResource extends CoapResource {
public TemperatureResource() {
super("temperature");
}
#Override
public void handleGET(CoapExchange exchange) {
// as the structure is fixed we know that two levels up
// there is the room, and four levels up there is the house
String room = getParent().getParent().getName();
String house = getParent().getParent().getParent().getParent().getName();
exchange.respond("The temperature of the " + house + " in the " + room + " is : 25 degree");
}
}
}
In that example the resources are dynamically created if they did not exist before. This could be also exchanged with some lookup or register mechanism (e.g. a house is registered via PUT or PUSH).
Don't misunderstand me here. The solution by #Copernic seems to work and is probably a suitable solution for some scenarios (e.g. each house has its own server and the requests need to be redirected), but for a rather simple scenario it looks to me that it is not the correct way to go.
You can override deliverRequest as stated by Alex, However my approach is that I don't pre-register the resources tree, I register resource by resource without maintaining a hierarchy.
public DynamicMessageDeliverer (List<ProxyRes> resources) {
this.resources = resources;
}
public void deliverRequest (final Exchange exchange) {
Request request = exchange.getRequest ();
List<String> path = request.getOptions ().getUriPath ();
final Resource resource = registerResources (path);
if (resource != null) {
executeResource (exchange, resource);
} else {
exchange.sendResponse (new Response (ResponseCode.NOT_FOUND));
throw new RuntimeException ("Did not find resource " + path.toString() + " requested by " + request.getSource()+":"+request.getSourcePort());
}
}
private void executeResource (final Exchange exchange, final Resource resource) {
// Get the executor and let it process the request
Executor executor = resource.getExecutor ();
if (executor != null) {
exchange.setCustomExecutor ();
executor.execute (new Runnable () {
public void run () {
resource.handleRequest (exchange);
}
});
} else {
resource.handleRequest (exchange);
}
}
private Resource registerResources (List<String> list) {
LinkedList<String> path = new LinkedList<String> (list);
String flatRequestedEndpoint = Arrays.toString (path.toArray ());
LinkedList<String> wildcards = new LinkedList <String> ();
ProxyRes retainedResource = null;
for (ProxyRes proxyRes : resources) {
String[] res = proxyRes.getPath ().replaceFirst ("/", "").split ("/");
int length = res.length;
if (length != path.size ()) {
continue;
}
String flatResEndpoint = Arrays.toString (res);
if (flatResEndpoint.equals (flatRequestedEndpoint)) {
retainedResource = proxyRes;
break;
}
boolean match = true;
for (int i = 0; i < length; i ++) {
String str = res[i];
if (str.equals ("*")) {
wildcards.add (path.get (i));
continue;
}
if (!str.equals (path.get (i))) {
match = false;
break;
}
}
if (!match) {
wildcards.clear ();
continue;
}
retainedResource = proxyRes;
break;
}
if (retainedResource == null) {
return null;
}
((AbstractResource)retainedResource.getCoapRes ()).setWildcard (wildcards);
return retainedResource.getCoapRes ();
}
Full answer code with steps is here : Eclipse Californium CoAP wildcard as url path

Creating testcases using nunit and moq on WEB API controllers

I am trying to create a unit test cases using nunit on controllers in an existing Web API project.
I am using Moq as mocking framework. In here I would like to mock repository to return some expected
result.I am not sure what I can do at the point of setting up of repository mockup and validating result.
Can somebody suggest if whatever I am doing it right ? I just took a random controller for this question.
Any advise or guidance to some example url would be greatly appreciated.
[TestCase]
public void TestMethod1()
{
AccountController ac = new AccountController();
var mockAuthRepository = new Mock<AuthRepository>();
//mockAuthRepository.Setup(m=>m.RegisterUser(It.IsAny<UserModel>))
}
[RoutePrefix("api/Account")]
public class AccountController : ApiController
{
private readonly AuthRepository _repo;
public AccountController()
{
_repo = new AuthRepository();
}
}
[HttpPost]
[Authorize(Users = "admin")]
[Route("Register")]
public async Task<IHttpActionResult> Register(UserModel userModel)
{
IdentityResult result = await _repo.RegisterUser(userModel);
IHttpActionResult errorResult = WrapError(result);
if (errorResult != null)
{
return errorResult;
}
return Ok();
}
private IHttpActionResult WrapError(IdentityResult result)
{
if (result == null)
{
return InternalServerError();
}
if (!result.Succeeded)
{
if (result.Errors != null)
{
foreach (string err in result.Errors)
{
ModelState.AddModelError("", err);
}
}
if (ModelState.IsValid)
{
return BadRequest();
}
return BadRequest(ModelState);
}
return null;
}
In your any specific test use setups like these:
mockAuthRepository
.Setup(m => m.RegisterUser(It.IsAny<UserModel>))
.Returns(new List<UserModel> {
new UserModel { // specific properties },
new UserModel { // specific properties },
new UserModel { // specific properties }
}.AsQueryable());
And then just make assertion for selected users.
For example, you can test Register method to prevent registering existing user by placing in mockAuthRepository the same user that was in controller input and assert that specific error code is generated.
You can also use mocks to check if some code is called by using Verify method with param Times.Once, Times.Never etc..
Good luck!

How to use Prime31 Google In App Billing Plugin

I just bought "Google In App Billing Plugin for unity3d" by Prime31. I don't understand how to use it in the game that I want to develop.
Can you please show me a code example? I understand I need to use my app key, but I don't know what to do next.
And how to a I make test purchases with this plugin?
Please help as much as you can, because I am really stuck on this subject for quite a while.
This is some of my "purchase maker" object called MoneyTakerScript (inherits from MonoBehaviour):
void Start()
{
string key = "My App Key...";
GoogleIAB.init(key);
var skus = new string[] { "cl.48931", "tp.58932", "mmm.68393" };
GoogleIAB.queryInventory( skus );
TPS = GameObject.Find("TPBtn").GetComponent(typeof(TPScript)) as TPScript;
CLS = GameObject.Find("CLBtn").GetComponent(typeof(CLScript)) as CLScript;
MMM = GameObject.Find("MMBtn").GetComponent(typeof(MMMScript)) as MMMScript;
}
public void Purchase(string ProductId)
{
GoogleIAB.purchaseProduct(ProductId);
}
public void UseProduct(string ProductId)
{
if (ProductId.Contains("cl"))
{
CLS.MakeCL();
}
if (ProductId.Contains("tp"))
{
TPS.MakeTP();
}
if (ProductId.Contains("mmm"))
{
MMM.MakeMMM();
}
GoogleIAB.consumeProduct(ProductId);
}
And this is some of my "purchase listner" object code:
void purchaseSucceededEvent(GooglePurchase purchase)
{
//Debug.Log( "purchaseSucceededEvent: " + purchase );
MoneyScript.UseProduct(purchase.productId);
}
void Start()
{
MoneyScript = GameObject.Find("MoneyTaker").GetComponent(typeof(MoneyTakerScript)) as
MoneyTakerScript;
}
I found the problem and solved it!
This line was missing in my AndroidManifest.Xml for some reason:
<activity android:name="com.prime31.GoogleIABProxyActivity"></activity>
Just added the line and now I have In App Purchase!!