google-wallet / rest-samples

Samples for the Google Wallet REST APIs
https://developers.google.com/wallet/
Apache License 2.0
277 stars 119 forks source link

Loyalty Pass update when data changes .NET Core #48

Closed DboukAli98 closed 1 year ago

DboukAli98 commented 1 year ago

Hello, I have successfully integrated the Google Wallet Loyalty Pass into my .NET Core web application and I have passed to it some dynamic data like points, user tier, etc... However, these data will get updated upon redeeming offers or winning points. I need to know how I can update the loyalty card when the data changes, I read the official documentation of Google Wallet Official Documentation - Update A Loyalty Card but it has nothing technical and it's very ambiguous. If anyone could please help me understand it better. Thanks in advance.

ncalteen commented 1 year ago

Hello,

I'd be happy to help out with that! It's on my task list to include code samples for this, notifications, and a few other use cases, so I will update this repo soon and reflect this in the documentation. In the meantime, there are a couple different options for updating loyalty cards...

update vs. patch methods

At the beginning, I have to stress the difference here. When you use the update method for a class or object, that replaces the existing configuration completely. This is best when you plan to completely change the way the class or object works, and you want to ensure no old information is left behind.

When you use the patch method, only the fields you include in the request are updated; existing fields will not be changed unless they're included in the request. This is best when you want to update some fields, but not all (such as changing a user's points balance).

I'll include examples of patch method calls below. If you're looking to do an update instead, the code would be the same except for the method being called. However, with an update, you need to include the full class or object definition.

Update a class

Any updates to a LoyaltyClass object will affect all users who have a loyalty card with that classId. This is useful when you want to do things that affect all card holders, such as add messages to the card details or update the hero image that is displayed on the card.

using Google.Apis.Walletobjects.v1.Data;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

string issuerId = "ISSUER_ID";
string classSuffix = "demo_loyalty";

// Ignore null values when serializing to/from JSON
JsonSerializerSettings excludeNulls = new JsonSerializerSettings()
{
  NullValueHandling = NullValueHandling.Ignore
};

// Create the demo class instance
DemoLoyalty demo = new DemoLoyalty();

// Create the authenticated HTTP client
demo.Auth();

// Check if the class already exists
string classId = $"{issuerId}.{classSuffix}";

Stream responseStream = DemoLoyalty.service.Loyaltyclass
            .Get(classId)
            .ExecuteAsStream();
StreamReader responseReader = new StreamReader(responseStream);
JObject jsonResponse = JObject.Parse(responseReader.ReadToEnd());

if (jsonResponse.ContainsKey("error"))
{
  if (jsonResponse["error"].Value<int>("code") == 404)
  {
    // Class does not exist
    Console.WriteLine($"Class {classId} not found!");
  }
  else
  {
    // Something else went wrong...
    Console.WriteLine(jsonResponse.ToString());
  }
}
else
{
  // Class exists
  // Update the loyalty program logo and name
  // ReviewStatus must be DRAFT or UNDER_REVIEW
  LoyaltyClass patchClass = new LoyaltyClass()
  {
    ReviewStatus = "UNDER_REVIEW",
    LocalizedProgramName = new LocalizedString()
    {
      DefaultValue = new TranslatedString()
      {
        Language = "en-US",
        Value = "Nick's Coffee and Iced Tea"
      }
    },
    ProgramLogo = new Image
    {
      SourceUri = new ImageUri
      {
        Uri = "http://farm8.staticflickr.com/7340/11177041185_a61a7f2139_o.jpg"
      },
      ContentDescription = new LocalizedString
      {
        DefaultValue = new TranslatedString
        {
          Language = "en-US",
          Value = "Logo description"
        }
      }
    }
  };

  responseStream = DemoLoyalty.service.Loyaltyclass
            .Patch(patchClass, classId)
            .ExecuteAsStream();
  responseReader = new StreamReader(responseStream);
  jsonResponse = JObject.Parse(responseReader.ReadToEnd());

  Console.WriteLine("Class patch response");
  Console.WriteLine(jsonResponse.ToString());
}

Update an object

The process is much the same as when you update a class, however updating an object only affects the user(s) who have saved that specific object to their Google Wallet app. I think this aligns with the use case you mentioned, where you'd like to update a user's points balance.

using Google.Apis.Walletobjects.v1.Data;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

string issuerId = "ISSUER_ID";
string objectSuffix = "demo_object";

// Ignore null values when serializing to/from JSON
JsonSerializerSettings excludeNulls = new JsonSerializerSettings()
{
  NullValueHandling = NullValueHandling.Ignore
};

// Create the demo class instance
DemoLoyalty demo = new DemoLoyalty();

// Create the authenticated HTTP client
demo.Auth();

// Check if the object already exists
string objectId = $"{issuerId}.{objectSuffix}";

Stream responseStream = DemoLoyalty.service.Loyaltyobject
            .Get(objectId)
            .ExecuteAsStream();
StreamReader responseReader = new StreamReader(responseStream);
JObject jsonResponse = JObject.Parse(responseReader.ReadToEnd());

if (jsonResponse.ContainsKey("error"))
{
  if (jsonResponse["error"].Value<int>("code") == 404)
  {
    // Object does not exist
    Console.WriteLine($"Object {objectId} not found!");
  }
  else
  {
    // Something else went wrong...
    Console.WriteLine(jsonResponse.ToString());
  }
}
else
{
  // Object exists
  // Update the user's point's balance (e.g. they earned 100 points)
  // Previous balance can be found in the GET response
  LoyaltyObject patchObject = new LoyaltyObject()
  {
    LoyaltyPoints = new LoyaltyPoints()
    {
      Balance = new LoyaltyPointsBalance()
      {
        Int__ = jsonResponse["loyaltyPoints"]["balance"].Value<int>("int") + 100
      }
    }
  };

  responseStream = DemoLoyalty.service.Loyaltyobject
            .Patch(patchObject, objectId)
            .ExecuteAsStream();
  responseReader = new StreamReader(responseStream);
  jsonResponse = JObject.Parse(responseReader.ReadToEnd());

  Console.WriteLine("Object patch response");
  Console.WriteLine(jsonResponse.ToString());
}

I hope this information helps! I'll work on adding these as more official samples into the repo and updating the documentation :)

DboukAli98 commented 1 year ago

Thanks @ncalteen for your help

Techboy000 commented 1 year ago

@ncalteen Hey :-) Could you please post the same example for PHP. I also struggle to update passes (object). Thank you.

Techboy000 commented 1 year ago

@ncalteen Hey :-) Could you please post the same example for PHP. I also struggle to update passes (object). Thank you.

Okay found it. $response = $this->service->loyaltyobject->update($objectId, $loyaltyObject); Thanks.

ncalteen commented 1 year ago

That's exactly it :) I'm working on adding these examples to the repo for each language, as well as a few others I noted are missing.

If you need help getting a complete example for updating passes via PHP please let me know and I can add something here in the meantime!