Skip to content

Commit e942b16

Browse files
Erik Bylundkirre-bylund
authored andcommitted
Add support for Steam IAP
1 parent c071b82 commit e942b16

File tree

3 files changed

+314
-2
lines changed

3 files changed

+314
-2
lines changed

Runtime/Client/LootLockerEndPoints.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ public class LootLockerEndPoints
206206
public static EndPointClass redeemAppleAppStorePurchase = new EndPointClass("store/apple/redeem", LootLockerHTTPMethod.POST);
207207
public static EndPointClass redeemGooglePlayStorePurchase = new EndPointClass("store/google/redeem", LootLockerHTTPMethod.POST);
208208

209+
public static EndPointClass beginSteamPurchaseRedemption = new EndPointClass("store/steam/redeem/begin", LootLockerHTTPMethod.POST);
210+
public static EndPointClass querySteamPurchaseRedemptionStatus = new EndPointClass("store/steam/redeem/query", LootLockerHTTPMethod.POST);
211+
public static EndPointClass finalizeSteamPurchaseRedemption = new EndPointClass("store/steam/redeem/finalise", LootLockerHTTPMethod.POST);
212+
209213
// EventTrigger
210214
[Header("EventTrigger")]
211215
public static EndPointClass triggeringAnEvent = new EndPointClass("v1/player/trigger", LootLockerHTTPMethod.POST);

Runtime/Game/LootLockerSDKManager.cs

Lines changed: 211 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
using LootLocker.LootLockerEnums;
77
using System.Linq;
88
using System.Security.Cryptography;
9+
#if LOOTLOCKER_USE_NEWTONSOFTJSON
10+
using Newtonsoft.Json;
11+
using Newtonsoft.Json.Linq;
12+
#else
13+
using LLlibs.ZeroDepJson;
14+
#endif
915
#if UNITY_EDITOR
1016
using UnityEditor;
1117
#endif
@@ -4394,7 +4400,7 @@ public static void PollOrderStatus(int assetId, Action<LootLockerPurchaseOrderSt
43944400
/// <summary>
43954401
/// Activate a rental asset. This will grant the asset to the player and start the rental timer on the server.
43964402
/// </summary>
4397-
/// <param name="assetId">The asset instance ID of the asset to activate</param>
4403+
/// <param name="assetInstanceID">The asset instance ID of the asset to activate</param>
43984404
/// <param name="onComplete">onComplete Action for handling the response of type LootLockerActivateARentalAssetResponse</param>
43994405
public static void ActivateRentalAsset(int assetInstanceID, Action<LootLockerActivateRentalAssetResponse> onComplete)
44004406
{
@@ -4522,7 +4528,210 @@ public static void RedeemGooglePlayStorePurchaseForClass(string productId, strin
45224528
LootLockerServerRequest.CallAPI(LootLockerEndPoints.redeemGooglePlayStorePurchase.endPoint, LootLockerEndPoints.redeemGooglePlayStorePurchase.httpMethod, body, onComplete: (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); });
45234529
}
45244530

4525-
#endregion
4531+
/// <summary>
4532+
/// Begin a Steam purchase with the given settings that when finalized will redeem the specified catalog item
4533+
///
4534+
/// Steam in-app purchases need to be configured for this to work
4535+
/// Steam in-app purchases works slightly different from other platforms, you begin a purchase with this call which initiates it in Steams backend
4536+
/// While your app is waiting for the user to finalize that purchase you can use QuerySteamPurchaseRedemptionStatus to get the status, when that tells you that the purchase is Approved you can finalize the purchase using FinalizeSteamPurchaseRedemption
4537+
/// </summary>
4538+
/// <param name="steamId">Id of the Steam User that is making the purchase</param>
4539+
/// <param name="currency">The currency to use for the purchase</param>
4540+
/// <param name="language">The language to use for the purchase</param>
4541+
/// <param name="catalogItemId">The LootLocker Catalog Item Id for the item you wish to purchase</param>
4542+
/// <param name="onComplete">onComplete Action for handling the response</param>
4543+
public static void BeginSteamPurchaseRedemption(string steamId, string currency, string language, string catalogItemId, Action<LootLockerBeginSteamPurchaseRedemptionResponse> onComplete)
4544+
{
4545+
if (!CheckInitialized())
4546+
{
4547+
onComplete?.Invoke(LootLockerResponseFactory.SDKNotInitializedError<LootLockerBeginSteamPurchaseRedemptionResponse>());
4548+
return;
4549+
}
4550+
var body = LootLockerJson.SerializeObject(new LootLockerBeginSteamPurchaseRedemptionRequest()
4551+
{
4552+
steam_id = steamId,
4553+
currency = currency,
4554+
language = language,
4555+
catalog_item_id = catalogItemId
4556+
});
4557+
4558+
LootLockerServerRequest.CallAPI(LootLockerEndPoints.beginSteamPurchaseRedemption.endPoint, LootLockerEndPoints.beginSteamPurchaseRedemption.httpMethod, body, onComplete:
4559+
(serverResponse) =>
4560+
{
4561+
var parsedResponse = LootLockerResponse.Deserialize<LootLockerBeginSteamPurchaseRedemptionResponse>(serverResponse);
4562+
if (!parsedResponse.success)
4563+
{
4564+
onComplete?.Invoke(parsedResponse);
4565+
return;
4566+
}
4567+
4568+
#if LOOTLOCKER_USE_NEWTONSOFTJSON
4569+
JObject jsonObject;
4570+
try
4571+
{
4572+
jsonObject = JObject.Parse(serverResponse.text);
4573+
}
4574+
catch (JsonReaderException)
4575+
{
4576+
onComplete?.Invoke(parsedResponse);
4577+
return;
4578+
}
4579+
if (jsonObject != null && jsonObject.TryGetValue("success", StringComparison.OrdinalIgnoreCase, out var successObj))
4580+
{
4581+
if (successObj.ToObject(typeof(bool)) is bool isSuccess)
4582+
{
4583+
parsedResponse.isSuccess = isSuccess;
4584+
}
4585+
}
4586+
#else
4587+
Dictionary<string, object> jsonObject = null;
4588+
try
4589+
{
4590+
jsonObject = Json.Deserialize(serverResponse.text) as Dictionary<string, object>;
4591+
}
4592+
catch (JsonException)
4593+
{
4594+
onComplete?.Invoke(parsedResponse);
4595+
return;
4596+
}
4597+
if (jsonObject != null && jsonObject.TryGetValue("success", out var successObj))
4598+
{
4599+
if (successObj is bool isSuccess)
4600+
{
4601+
parsedResponse.isSuccess = isSuccess;
4602+
}
4603+
}
4604+
#endif
4605+
onComplete?.Invoke(parsedResponse);
4606+
});
4607+
}
4608+
4609+
/// <summary>
4610+
/// Begin a Steam purchase with the given settings that when finalized will redeem the specified catalog item for the specified class
4611+
///
4612+
/// Steam in-app purchases need to be configured for this to work
4613+
/// Steam in-app purchases works slightly different from other platforms, you begin a purchase with this call which initiates it in Steams backend
4614+
/// While your app is waiting for the user to finalize that purchase you can use QuerySteamPurchaseRedemptionStatus to get the status, when that tells you that the purchase is Approved you can finalize the purchase using FinalizeSteamPurchaseRedemption
4615+
/// </summary>
4616+
/// <param name="classId">Id of the class to make the purchase for</param>
4617+
/// <param name="steamId">Id of the Steam User that is making the purchase</param>
4618+
/// <param name="currency">The currency to use for the purchase</param>
4619+
/// <param name="language">The language to use for the purchase</param>
4620+
/// <param name="catalogItemId">The LootLocker Catalog Item Id for the item you wish to purchase</param>
4621+
/// <param name="onComplete">onComplete Action for handling the response</param>
4622+
public static void BeginSteamPurchaseRedemptionForClass(int classId, string steamId, string currency, string language, string catalogItemId, Action<LootLockerBeginSteamPurchaseRedemptionResponse> onComplete)
4623+
{
4624+
if (!CheckInitialized())
4625+
{
4626+
onComplete?.Invoke(LootLockerResponseFactory.SDKNotInitializedError<LootLockerBeginSteamPurchaseRedemptionResponse>());
4627+
return;
4628+
}
4629+
var body = LootLockerJson.SerializeObject(new LootLockerBeginSteamPurchaseRedemptionForClassRequest()
4630+
{
4631+
class_id = classId,
4632+
steam_id = steamId,
4633+
currency = currency,
4634+
language = language,
4635+
catalog_item_id = catalogItemId
4636+
});
4637+
4638+
LootLockerServerRequest.CallAPI(LootLockerEndPoints.beginSteamPurchaseRedemption.endPoint, LootLockerEndPoints.beginSteamPurchaseRedemption.httpMethod, body, onComplete:
4639+
(serverResponse) =>
4640+
{
4641+
var parsedResponse = LootLockerResponse.Deserialize<LootLockerBeginSteamPurchaseRedemptionResponse>(serverResponse);
4642+
if (!parsedResponse.success)
4643+
{
4644+
onComplete?.Invoke(parsedResponse);
4645+
return;
4646+
}
4647+
4648+
#if LOOTLOCKER_USE_NEWTONSOFTJSON
4649+
JObject jsonObject;
4650+
try
4651+
{
4652+
jsonObject = JObject.Parse(serverResponse.text);
4653+
}
4654+
catch (JsonReaderException)
4655+
{
4656+
onComplete?.Invoke(parsedResponse);
4657+
return;
4658+
}
4659+
if (jsonObject != null && jsonObject.TryGetValue("success", StringComparison.OrdinalIgnoreCase, out var successObj))
4660+
{
4661+
if (successObj.ToObject(typeof(bool)) is bool isSuccess)
4662+
{
4663+
parsedResponse.isSuccess = isSuccess;
4664+
}
4665+
}
4666+
#else
4667+
Dictionary<string, object> jsonObject = null;
4668+
try
4669+
{
4670+
jsonObject = Json.Deserialize(serverResponse.text) as Dictionary<string, object>;
4671+
}
4672+
catch (JsonException)
4673+
{
4674+
onComplete?.Invoke(parsedResponse);
4675+
return;
4676+
}
4677+
if (jsonObject != null && jsonObject.TryGetValue("success", out var successObj))
4678+
{
4679+
if (successObj is bool isSuccess)
4680+
{
4681+
parsedResponse.isSuccess = isSuccess;
4682+
}
4683+
}
4684+
#endif
4685+
onComplete?.Invoke(parsedResponse);
4686+
});
4687+
}
4688+
4689+
/// <summary>
4690+
/// Check the Steam Purchase status for a given entitlement
4691+
///
4692+
/// Use this to check the status of an ongoing purchase to know when it's ready to finalize or has been aborted
4693+
/// or use this to get information for a completed purchase
4694+
/// </summary>
4695+
/// <param name="entitlementId">The id of the entitlement to check the status for</param>
4696+
/// <param name="onComplete">onComplete Action for handling the response</param>
4697+
public static void BeginSteamPurchaseRedemption(string entitlementId, Action<LootLockerQuerySteamPurchaseRedemptionStatusResponse> onComplete)
4698+
{
4699+
if (!CheckInitialized())
4700+
{
4701+
onComplete?.Invoke(LootLockerResponseFactory.SDKNotInitializedError<LootLockerQuerySteamPurchaseRedemptionStatusResponse>());
4702+
return;
4703+
}
4704+
var body = LootLockerJson.SerializeObject(new LootLockerQuerySteamPurchaseRedemptionStatusRequest()
4705+
{
4706+
entitlement_id = entitlementId
4707+
});
4708+
4709+
LootLockerServerRequest.CallAPI(LootLockerEndPoints.querySteamPurchaseRedemptionStatus.endPoint, LootLockerEndPoints.querySteamPurchaseRedemptionStatus.httpMethod, body, onComplete: (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); });
4710+
}
4711+
4712+
/// <summary>
4713+
/// Finalize a started Steam Purchase and subsequently redeem the catalog items that the entitlement refers to
4714+
///
4715+
/// The steam purchase needs to be in status Approved for this call to work
4716+
/// </summary>
4717+
/// <param name="entitlementId">The id of the entitlement to finalize the purchase for</param>
4718+
/// <param name="onComplete">onComplete Action for handling the response</param>
4719+
public static void FinalizeSteamPurchaseRedemption(string entitlementId, Action<LootLockerResponse> onComplete)
4720+
{
4721+
if (!CheckInitialized())
4722+
{
4723+
onComplete?.Invoke(LootLockerResponseFactory.SDKNotInitializedError<LootLockerResponse>());
4724+
return;
4725+
}
4726+
var body = LootLockerJson.SerializeObject(new LootLockerFinalizeSteamPurchaseRedemptionRequest()
4727+
{
4728+
entitlement_id = entitlementId
4729+
});
4730+
4731+
LootLockerServerRequest.CallAPI(LootLockerEndPoints.finalizeSteamPurchaseRedemption.endPoint, LootLockerEndPoints.finalizeSteamPurchaseRedemption.httpMethod, body, onComplete: (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); });
4732+
}
4733+
4734+
#endregion
45264735

45274736
#region Collectables
45284737
/// <summary>

Runtime/Game/Requests/PurchaseRequest.cs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,28 @@
11
using LootLocker.Requests;
22
using System;
3+
using LootLocker.LootLockerEnums;
34
#if LOOTLOCKER_USE_NEWTONSOFTJSON
45
using Newtonsoft.Json;
56
#else
67
using LLlibs.ZeroDepJson;
78
#endif
89

10+
namespace LootLocker.LootLockerEnums
11+
{
12+
public enum SteamPurchaseRedemptionStatus
13+
{
14+
Init,
15+
Approved,
16+
Succeeded,
17+
Failed,
18+
Refunded,
19+
PartialRefund,
20+
ChargedBack,
21+
RefundedSuspectedFraud,
22+
RefundedFriendlyFraud
23+
}
24+
}
25+
926
namespace LootLocker.Requests
1027
{
1128
#region Legacy Purchasing
@@ -148,6 +165,88 @@ public class LootLockerRedeemGooglePlayStorePurchaseForClassRequest : LootLocker
148165
#endif
149166
public int class_id { get; set; }
150167
}
168+
169+
/// <summary>
170+
///
171+
/// </summary>
172+
public class LootLockerBeginSteamPurchaseRedemptionRequest
173+
{
174+
/// <summary>
175+
/// Id of the Steam User that is making the purchase
176+
/// </summary>
177+
public string steam_id { get; set; }
178+
/// <summary>
179+
/// The currency to use for the purchase
180+
/// </summary>
181+
public string currency { get; set; }
182+
/// <summary>
183+
/// The language to use for the purchase
184+
/// </summary>
185+
public string language { get; set; }
186+
/// <summary>
187+
/// The LootLocker Catalog Item Id for the item you wish to purchase
188+
/// </summary>
189+
public string catalog_item_id { get; set; }
190+
}
191+
192+
/// <summary>
193+
///
194+
/// </summary>
195+
public class LootLockerBeginSteamPurchaseRedemptionForClassRequest : LootLockerBeginSteamPurchaseRedemptionRequest
196+
{
197+
/// <summary>
198+
/// Id of the class to make the purchase for
199+
/// </summary>
200+
public int class_id { get; set; }
201+
}
202+
203+
/// <summary>
204+
///
205+
/// </summary>
206+
public class LootLockerBeginSteamPurchaseRedemptionResponse : LootLockerResponse
207+
{
208+
/// <summary>
209+
/// Was the purchase redemption process started successfully
210+
/// </summary>
211+
public bool isSuccess { get; set; }
212+
/// <summary>
213+
/// The id of the entitlement this purchase relates to
214+
/// </summary>
215+
public string entitlement_id { get; set; }
216+
}
217+
218+
/// <summary>
219+
///
220+
/// </summary>
221+
public class LootLockerQuerySteamPurchaseRedemptionStatusRequest
222+
{
223+
/// <summary>
224+
/// The id of the entitlement to check the status for
225+
/// </summary>
226+
public string entitlement_id { get; set; }
227+
}
228+
229+
/// <summary>
230+
///
231+
/// </summary>
232+
public class LootLockerQuerySteamPurchaseRedemptionStatusResponse : LootLockerResponse
233+
{
234+
/// <summary>
235+
/// The status of the steam purchase
236+
/// </summary>
237+
public SteamPurchaseRedemptionStatus status { get; set; }
238+
}
239+
240+
/// <summary>
241+
///
242+
/// </summary>
243+
public class LootLockerFinalizeSteamPurchaseRedemptionRequest
244+
{
245+
/// <summary>
246+
/// The id of the entitlement to finalize the purchase for
247+
/// </summary>
248+
public string entitlement_id { get; set; }
249+
}
151250
}
152251

153252
namespace LootLocker

0 commit comments

Comments
 (0)