4 Commits
0.1.0 ... upm

Author SHA1 Message Date
Savya Bikram Shah
1f6ad3df13 chore(upm): release 0.2.1 and run split on macos host runner
Bump package to 0.2.1 (maintenance/publish, no API changes) and repoint
the Publish UPM workflow from ubuntu-latest to the macos host runner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 11:53:21 +05:45
Savya Bikram Shah
cca555f3e9 sample fixes 2026-05-07 18:44:59 +05:45
Savya Bikram Shah
4fe63fc575 fix: add UniTask ref to runtime test asmdef 2026-05-07 18:13:09 +05:45
Savya Bikram Shah
6b8815051d feat: route FonepayClient through FonepayAsyncBridge; bump 0.2.0 2026-05-07 18:10:17 +05:45
12 changed files with 1619 additions and 40 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -4,6 +4,16 @@ All notable changes to this package will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [0.2.1] - 2026-06-16
### Changed
- Maintenance release to publish the package via the UPM split pipeline. No API or runtime changes.
## [0.2.0] - 2026-05-07
### Changed
- All `FonepayClient` public methods now return `FonepayAsync<T>` / `FonepayAsync` instead of `Task<T>` / `Task`. Callers `await` the same way; UniTask users get native `UniTask` under `#if UNITASK_SUPPORT`.
## [0.1.0] - 2026-05-07 ## [0.1.0] - 2026-05-07
### Added ### Added

View File

@@ -4,10 +4,6 @@ using System.Threading.Tasks;
namespace Darkmatter.Fonepay namespace Darkmatter.Fonepay
{ {
/// <summary>
/// Public facade. Auto-loads config + secrets via <see cref="FonepayConfig.Load"/>.
/// Caller never touches credentials.
/// </summary>
public sealed class FonepayClient public sealed class FonepayClient
{ {
private readonly FonepayApiClient _api; private readonly FonepayApiClient _api;
@@ -22,41 +18,25 @@ namespace Darkmatter.Fonepay
_api = new FonepayApiClient(config, signer); _api = new FonepayApiClient(config, signer);
} }
/// <summary> public FonepayAsync<QrResult> PurchaseAsync(QrRequest req, CancellationToken ct = default)
/// Request a new QR code. The returned URL is valid for 15 minutes. Call GetStatusAsync() to check if the QR code has been paid. => FonepayAsyncBridge.Wrap(_api.PostQRAsync(req, ct), ct);
/// </summary>
/// <param name="req"></param>
/// <param name="ct"></param>
/// <returns></returns>
public Task<QrResult> PurchaseAsync(QrRequest req, CancellationToken ct = default)
=> _api.PostQRAsync(req, ct);
/// <summary> public FonepayAsync<QrResult> GetStatusAsync(string prn, CancellationToken ct = default)
/// Check if a QR code has been paid. Call after PostQRAsync() to check if the QR code has been paid. Returns "PAID" if successful, "UNPAID" if not yet paid, or an error message if the PRN is invalid or expired. => FonepayAsyncBridge.Wrap(_api.GetStatusAsync(prn, ct), ct);
/// </summary>
/// <param name="prn"></param>
/// <param name="ct"></param>
/// <returns></returns>
public Task<QrResult> GetStatusAsync(string prn, CancellationToken ct = default)
=> _api.GetStatusAsync(prn, ct);
/// <summary> public FonepayAsync<TaxRefundResponse> PostTaxRefundAsync(TaxRefundRequest req, CancellationToken ct = default)
/// Request a tax refund. Returns "REFUND_SUCCESS" if successful, or an error message if the PRN is invalid, expired, or not eligible for refund. => FonepayAsyncBridge.Wrap(_api.PostTaxRefundAsync(req, ct), ct);
/// </summary>
/// <param name="req"></param>
/// <param name="ct"></param>
/// <returns></returns>
public Task<TaxRefundResponse> PostTaxRefundAsync(TaxRefundRequest req, CancellationToken ct = default)
=> _api.PostTaxRefundAsync(req, ct);
/// <summary> public FonepayAsync<QRPaymentStatus> AwaitPaymentAsync(
/// Connects to the QR websocket and awaits the terminal payment frame, then auto-disconnects.
/// Pass <see cref="QrResult.thirdpartyQrWebSocketUrl"/> from <see cref="PurchaseAsync"/>.
/// </summary>
public async Task<QRPaymentStatus> AwaitPaymentAsync(
string websocketUrl, string websocketUrl,
Action<bool> onQrVerified = null, Action<bool> onQrVerified = null,
CancellationToken ct = default) CancellationToken ct = default)
=> FonepayAsyncBridge.Wrap(AwaitPaymentInternalAsync(websocketUrl, onQrVerified, ct), ct);
private async Task<QRPaymentStatus> AwaitPaymentInternalAsync(
string websocketUrl,
Action<bool> onQrVerified,
CancellationToken ct)
{ {
if (string.IsNullOrEmpty(websocketUrl)) if (string.IsNullOrEmpty(websocketUrl))
throw new ArgumentException("Websocket URL required", nameof(websocketUrl)); throw new ArgumentException("Websocket URL required", nameof(websocketUrl));

BIN
Samples~/.DS_Store vendored Normal file

Binary file not shown.

8
Samples~/Example.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 65d000fc5ca9742ec882f227d68c7bd4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

BIN
Samples~/Example/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -30,14 +30,18 @@ namespace Darkmatter.Fonepay.Samples
private async UniTask InitiatePayment() private async UniTask InitiatePayment()
{ {
if (_payCts != null) return;
payButton.interactable = false;
var fonepay = new FonepayClient(); var fonepay = new FonepayClient();
_payCts = CancellationTokenSource.CreateLinkedTokenSource(destroyCancellationToken); var cts = CancellationTokenSource.CreateLinkedTokenSource(destroyCancellationToken);
_payCts = cts;
try try
{ {
var qr = await fonepay.PurchaseAsync( var qr = await fonepay.PurchaseAsync(
new QrRequest { amount = amount, remarks1 = "sample" }, new QrRequest { amount = amount, remarks1 = "sample" },
_payCts.Token); cts.Token);
if (qr.qrCode != null) if (qr.qrCode != null)
{ {
@@ -51,7 +55,7 @@ namespace Darkmatter.Fonepay.Samples
var payment = await fonepay.AwaitPaymentAsync( var payment = await fonepay.AwaitPaymentAsync(
qr.thirdpartyQrWebSocketUrl, qr.thirdpartyQrWebSocketUrl,
onQrVerified: v => Debug.Log($"Fonepay QR verified: {v}"), onQrVerified: v => Debug.Log($"Fonepay QR verified: {v}"),
ct: _payCts.Token); ct: cts.Token);
Debug.Log($"Payment frame: {JsonUtility.ToJson(payment)}"); Debug.Log($"Payment frame: {JsonUtility.ToJson(payment)}");
ShowResult(payment.Outcome == PaymentOutcome.Complete); ShowResult(payment.Outcome == PaymentOutcome.Complete);
@@ -73,14 +77,19 @@ namespace Darkmatter.Fonepay.Samples
} }
finally finally
{ {
_payCts.Dispose(); cts.Dispose();
_payCts = null; if (_payCts == cts) _payCts = null;
if (this != null && payButton != null) payButton.interactable = true;
} }
} }
private void ShowResult(bool ok) private void ShowResult(bool ok)
{ {
qrImage.gameObject.SetActive(false); if (qrImage != null)
{
qrImage.sprite = null;
qrImage.gameObject.SetActive(false);
}
if (successObject != null) successObject.SetActive(ok); if (successObject != null) successObject.SetActive(ok);
if (failedObject != null) failedObject.SetActive(!ok); if (failedObject != null) failedObject.SetActive(!ok);
} }

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d53a9483205b945d69e846f2285560bd

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8c9cfa26abfee488c85f1582747f6a02
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -3,6 +3,7 @@
"rootNamespace": "", "rootNamespace": "",
"references": [ "references": [
"Darkmatter.FonepayUnity", "Darkmatter.FonepayUnity",
"UniTask",
"UnityEngine.TestRunner" "UnityEngine.TestRunner"
], ],
"includePlatforms": [], "includePlatforms": [],

View File

@@ -1,7 +1,7 @@
{ {
"name": "com.darkmattergameproduction.fonepay-unity", "name": "com.darkmattergameproduction.fonepay-unity",
"displayName": "Fonepay Unity", "displayName": "Fonepay Unity",
"version": "0.1.0", "version": "0.2.1",
"unity": "6000.4", "unity": "6000.4",
"unityRelease": "5f1", "unityRelease": "5f1",
"description": "Fonepay payment integration for Unity. Generate Fonepay QR codes, await payment confirmation via websocket, and process tax refunds. Credentials managed via Tools > Fonepay > Settings.", "description": "Fonepay payment integration for Unity. Generate Fonepay QR codes, await payment confirmation via websocket, and process tax refunds. Credentials managed via Tools > Fonepay > Settings.",