diff --git a/Assemblies/BitPay.Net.Lib/net452/BitPay.dll b/Assemblies/BitPay.Net.Lib/net452/BitPay.dll index e643dd5..c0a017f 100644 Binary files a/Assemblies/BitPay.Net.Lib/net452/BitPay.dll and b/Assemblies/BitPay.Net.Lib/net452/BitPay.dll differ diff --git a/Assemblies/BitPay.Net.Lib/net46/BitPay.dll b/Assemblies/BitPay.Net.Lib/net46/BitPay.dll index 8bd155d..23d0966 100644 Binary files a/Assemblies/BitPay.Net.Lib/net46/BitPay.dll and b/Assemblies/BitPay.Net.Lib/net46/BitPay.dll differ diff --git a/Assemblies/BitPay.Net.Lib/net461/BitPay.dll b/Assemblies/BitPay.Net.Lib/net461/BitPay.dll index 0fc1d64..343783f 100644 Binary files a/Assemblies/BitPay.Net.Lib/net461/BitPay.dll and b/Assemblies/BitPay.Net.Lib/net461/BitPay.dll differ diff --git a/Assemblies/BitPay.Net.Lib/net462/BitPay.dll b/Assemblies/BitPay.Net.Lib/net462/BitPay.dll index 1645791..d046e92 100644 Binary files a/Assemblies/BitPay.Net.Lib/net462/BitPay.dll and b/Assemblies/BitPay.Net.Lib/net462/BitPay.dll differ diff --git a/Assemblies/BitPay.Net.Lib/net47/BitPay.dll b/Assemblies/BitPay.Net.Lib/net47/BitPay.dll index 6afbc12..3d3e9b2 100644 Binary files a/Assemblies/BitPay.Net.Lib/net47/BitPay.dll and b/Assemblies/BitPay.Net.Lib/net47/BitPay.dll differ diff --git a/Assemblies/BitPay.Net.Lib/net471/BitPay.dll b/Assemblies/BitPay.Net.Lib/net471/BitPay.dll index 0917737..c098700 100644 Binary files a/Assemblies/BitPay.Net.Lib/net471/BitPay.dll and b/Assemblies/BitPay.Net.Lib/net471/BitPay.dll differ diff --git a/Assemblies/BitPay.Net.Lib/net472/BitPay.dll b/Assemblies/BitPay.Net.Lib/net472/BitPay.dll index e07d234..e3ba3fc 100644 Binary files a/Assemblies/BitPay.Net.Lib/net472/BitPay.dll and b/Assemblies/BitPay.Net.Lib/net472/BitPay.dll differ diff --git a/Assemblies/BitPay.Net.Lib/netcoreapp2.0/BitPay.deps.json b/Assemblies/BitPay.Net.Lib/netcoreapp2.0/BitPay.deps.json index 8149482..f847dfd 100644 --- a/Assemblies/BitPay.Net.Lib/netcoreapp2.0/BitPay.deps.json +++ b/Assemblies/BitPay.Net.Lib/netcoreapp2.0/BitPay.deps.json @@ -6,7 +6,7 @@ "compilationOptions": {}, "targets": { ".NETCoreApp,Version=v2.0": { - "BitPay/3.1.1912": { + "BitPay/3.2.2001": { "dependencies": { "BouncyCastle": "1.8.5", "Microsoft.CSharp": "4.5.0", @@ -147,7 +147,7 @@ } }, "libraries": { - "BitPay/3.1.1912": { + "BitPay/3.2.2001": { "type": "project", "serviceable": false, "sha512": "" diff --git a/Assemblies/BitPay.Net.Lib/netcoreapp2.0/BitPay.dll b/Assemblies/BitPay.Net.Lib/netcoreapp2.0/BitPay.dll index 343b482..dfb6d50 100644 Binary files a/Assemblies/BitPay.Net.Lib/netcoreapp2.0/BitPay.dll and b/Assemblies/BitPay.Net.Lib/netcoreapp2.0/BitPay.dll differ diff --git a/Assemblies/BitPay.Net.Lib/netcoreapp2.1/BitPay.deps.json b/Assemblies/BitPay.Net.Lib/netcoreapp2.1/BitPay.deps.json index 14dce8a..5ed59ad 100644 --- a/Assemblies/BitPay.Net.Lib/netcoreapp2.1/BitPay.deps.json +++ b/Assemblies/BitPay.Net.Lib/netcoreapp2.1/BitPay.deps.json @@ -6,7 +6,7 @@ "compilationOptions": {}, "targets": { ".NETCoreApp,Version=v2.1": { - "BitPay/3.1.1912": { + "BitPay/3.2.2001": { "dependencies": { "BouncyCastle": "1.8.5", "Microsoft.CSharp": "4.5.0", @@ -137,7 +137,7 @@ } }, "libraries": { - "BitPay/3.1.1912": { + "BitPay/3.2.2001": { "type": "project", "serviceable": false, "sha512": "" diff --git a/Assemblies/BitPay.Net.Lib/netcoreapp2.1/BitPay.dll b/Assemblies/BitPay.Net.Lib/netcoreapp2.1/BitPay.dll index 4194888..4932f2b 100644 Binary files a/Assemblies/BitPay.Net.Lib/netcoreapp2.1/BitPay.dll and b/Assemblies/BitPay.Net.Lib/netcoreapp2.1/BitPay.dll differ diff --git a/Assemblies/BitPay.Net.Lib/netcoreapp2.2/BitPay.deps.json b/Assemblies/BitPay.Net.Lib/netcoreapp2.2/BitPay.deps.json index 1c25a71..690f165 100644 --- a/Assemblies/BitPay.Net.Lib/netcoreapp2.2/BitPay.deps.json +++ b/Assemblies/BitPay.Net.Lib/netcoreapp2.2/BitPay.deps.json @@ -6,7 +6,7 @@ "compilationOptions": {}, "targets": { ".NETCoreApp,Version=v2.2": { - "BitPay/3.1.1912": { + "BitPay/3.2.2001": { "dependencies": { "BouncyCastle": "1.8.5", "Microsoft.CSharp": "4.5.0", @@ -137,7 +137,7 @@ } }, "libraries": { - "BitPay/3.1.1912": { + "BitPay/3.2.2001": { "type": "project", "serviceable": false, "sha512": "" diff --git a/Assemblies/BitPay.Net.Lib/netcoreapp2.2/BitPay.dll b/Assemblies/BitPay.Net.Lib/netcoreapp2.2/BitPay.dll index 9441f37..0e000a7 100644 Binary files a/Assemblies/BitPay.Net.Lib/netcoreapp2.2/BitPay.dll and b/Assemblies/BitPay.Net.Lib/netcoreapp2.2/BitPay.dll differ diff --git a/Assemblies/BitPay.Net.Lib/netstandard2.0/BitPay.deps.json b/Assemblies/BitPay.Net.Lib/netstandard2.0/BitPay.deps.json index fdb7586..fc34d76 100644 --- a/Assemblies/BitPay.Net.Lib/netstandard2.0/BitPay.deps.json +++ b/Assemblies/BitPay.Net.Lib/netstandard2.0/BitPay.deps.json @@ -7,7 +7,7 @@ "targets": { ".NETStandard,Version=v2.0": {}, ".NETStandard,Version=v2.0/": { - "BitPay/3.1.1912": { + "BitPay/3.2.2001": { "dependencies": { "BouncyCastle": "1.8.5", "Microsoft.CSharp": "4.5.0", @@ -180,7 +180,7 @@ } }, "libraries": { - "BitPay/3.1.1912": { + "BitPay/3.2.2001": { "type": "project", "serviceable": false, "sha512": "" diff --git a/Assemblies/BitPay.Net.Lib/netstandard2.0/BitPay.dll b/Assemblies/BitPay.Net.Lib/netstandard2.0/BitPay.dll index e5139c0..fac0f99 100644 Binary files a/Assemblies/BitPay.Net.Lib/netstandard2.0/BitPay.dll and b/Assemblies/BitPay.Net.Lib/netstandard2.0/BitPay.dll differ diff --git a/Assemblies/BitPay.Net.Lib_3.2.2001.zip b/Assemblies/BitPay.Net.Lib_3.2.2001.zip new file mode 100644 index 0000000..f7dbeae Binary files /dev/null and b/Assemblies/BitPay.Net.Lib_3.2.2001.zip differ diff --git a/BitPay/BitPay.cs b/BitPay/BitPay.cs index f80b39f..bec3c30 100644 --- a/BitPay/BitPay.cs +++ b/BitPay/BitPay.cs @@ -21,8 +21,8 @@ /** * @author Antonio Buedo - * @date 20.12.2019 - * @version 3.1.1912 + * @date 10.01.2020 + * @version 3.2.2001 * * See bitpay.com/api for more information. */ @@ -173,7 +173,7 @@ public string GetTokenByFacade(string facade) /// The facade to create the invoice against /// Allow unsigned request /// A new invoice object returned from the server. - public async Task CreateInvoice(Invoice invoice, string facade = Facade.PointOfSale, bool signRequest = true) + public async Task CreateInvoice(Invoice invoice, string facade = Facade.Merchant, bool signRequest = true) { try { @@ -277,6 +277,176 @@ public async Task> GetInvoices(DateTime dateStart, DateTime dateEn } } + /// + /// Create a refund for a BitPay invoice. + /// + /// A BitPay invoice object for which a refund request should be made. Must have been obtained using the merchant facade. + /// The email of the buyer to which the refund email will be sent. + /// The amount of money to refund. If zero then a request for 100% of the invoice value is created. + /// The three digit currency code specifying the exchange rate to use when calculating the refund bitcoin amount. If this value is "BTC" then no exchange rate calculation is performed. + /// ATrue if the refund was successfully created, false otherwise. + /// RefundCreationException RefundCreationException class + /// + public async Task CreateRefund(Invoice invoice, string refundEmail, double amount, string currency) { + + try + { + bool result; + Refund refund = new Refund(); + refund.Token = invoice.Token; + refund.Guid = Guid.NewGuid().ToString(); + refund.Amount = amount; + refund.RefundEmail = refundEmail; + refund.Currency = currency; + var json = JsonConvert.SerializeObject(refund); + var response = await Post("invoices/" + invoice.Id + "/refunds", json, true).ConfigureAwait(false); + var responseString = await ResponseToJsonString(response).ConfigureAwait(false); + JObject responseObject = JsonConvert.DeserializeObject(responseString); + bool.TryParse(responseObject.GetValue("success").ToString(), out result); + + return result; + } + catch (Exception ex) + { + if (!(ex.GetType().IsSubclassOf(typeof(BitPayException)) || ex.GetType() == typeof(BitPayException))) + throw new RefundCreationException(ex); + + throw; + } + } + + /// + /// Retrieve all refund requests on a BitPay invoice. + /// + /// The BitPay invoice object having the associated refunds. + /// A BitPay invoice object with the associated Refund objects updated. + /// RefundQueryException RefundQueryException class + /// + public async Task> GetRefunds(Invoice invoice) + { + try + { + var parameters = InitParams(); + parameters.Add("token", invoice.Token); + + var response = await Get("invoices/" + invoice.Id + "/refunds", parameters); + var responseString = await ResponseToJsonString(response); + + return JsonConvert.DeserializeObject>(responseString); + } + catch (Exception ex) + { + if (!(ex.GetType().IsSubclassOf(typeof(BitPayException)) || ex.GetType() == typeof(BitPayException))) + throw new RefundQueryException(ex); + + throw; + } + } + + /// + /// Retrieve a previously made refund request on a BitPay invoice. + /// + /// The BitPay invoice having the associated refund. + /// The refund id for the refund to be updated with new status. + /// TA BitPay invoice object with the associated Refund object updated. + /// RefundQueryException RefundQueryException class + public async Task GetRefund(Invoice invoice, string refundId) + { + try + { + var parameters = InitParams(); + parameters.Add("token", invoice.Token); + + var response = await Get("invoices/" + invoice.Id + "/refunds/" + refundId, parameters); + var responseString = await ResponseToJsonString(response); + + return JsonConvert.DeserializeObject(responseString); + } + catch (Exception ex) + { + if (!(ex.GetType().IsSubclassOf(typeof(BitPayException)) || ex.GetType() == typeof(BitPayException))) + throw new RefundQueryException(ex); + + throw; + } + } + + /// + /// Implements the CancelRefund method bellow. + /// + /// The BitPay invoice Id having the associated refund to be canceled. + /// The refund Id for the refund to be canceled. + /// ATrue if the refund was successfully canceled, false otherwise. + /// RefundCancellationException RefundCancellationException class + public async Task CancelRefund(string invoiceId, string refundId) + { + try + { + var invoice = await GetInvoice(invoiceId); + + return await CancelRefund(invoice, refundId); + } + catch (Exception ex) + { + if (!(ex.GetType().IsSubclassOf(typeof(BitPayException)) || ex.GetType() == typeof(BitPayException))) + throw new RefundCancellationException(ex); + + throw; + } + } + + /// + /// Implements the CancelRefund method bellow. + /// + /// The BitPay invoice having the associated refund to be canceled. Must have been obtained using the merchant facade. + /// The refund objhect for the refund to be canceled. + /// ATrue if the refund was successfully canceled, false otherwise. + /// RefundCancellationException RefundCancellationException class + public async Task CancelRefund(Invoice invoice, string refundId) + { + try + { + var refund = await GetRefund(invoice, refundId); + + return await CancelRefund(invoice.Id, refund); + } + catch (Exception ex) + { + if (!(ex.GetType().IsSubclassOf(typeof(BitPayException)) || ex.GetType() == typeof(BitPayException))) + throw new RefundCancellationException(ex); + + throw; + } + } + + /// + /// Cancel a previously submitted refund request on a BitPay invoice. + /// + /// The BitPay invoiceId having the associated refund to be canceled. + /// The BitPay refund for the refund to be canceled. + /// ATrue if the refund was successfully canceled, false otherwise. + /// RefundCancellationException RefundCancellationException class + public async Task CancelRefund(string invoiceId, Refund refund) + { + try + { + var parameters = InitParams(); + parameters.Add("token", refund.Token); + + var response = await Delete("invoices/" + invoiceId + "/refunds/" + refund.Id, parameters); + var responseString = await ResponseToJsonString(response).ConfigureAwait(false); + return responseString.Replace("\"", "").Equals("Success"); + } + catch (Exception ex) + { + if (!(ex.GetType().IsSubclassOf(typeof(BitPayException)) || ex.GetType() == typeof(BitPayException))) + throw new RefundCancellationException(ex); + + throw; + } + + } + /// /// Create a bill. /// diff --git a/BitPay/BitPay.csproj b/BitPay/BitPay.csproj index 4cf4893..a98a5dd 100644 --- a/BitPay/BitPay.csproj +++ b/BitPay/BitPay.csproj @@ -5,10 +5,10 @@ net452;net46;net461;net462;net47;net471;net472;netstandard2.0;netcoreapp2.0;netcoreapp2.1;netcoreapp2.2 latest - 3.1.1912.0 - 3.1.1912 - 3.1.1912 - 3.1.1912 + 3.2.2001.0 + 3.2.2001 + 3.2.2001 + 3.2.2001 Antonio Buedo BitPay Inc. BitPay, Inc. diff --git a/BitPay/Env.cs b/BitPay/Env.cs index 274ec22..64e63f8 100644 --- a/BitPay/Env.cs +++ b/BitPay/Env.cs @@ -7,7 +7,7 @@ public class Env public const string TestUrl = "https://test.bitpay.com/"; public const string ProdUrl = "https://bitpay.com/"; public const string BitpayApiVersion = "2.0.0"; - public const string BitpayPluginInfo = "BitPay_DotNet_Client_v3.1.1912"; + public const string BitpayPluginInfo = "BitPay_DotNet_Client_v3.2.2001"; public class Tokens { public string POS { get; set; } diff --git a/BitPay/Exceptions/RefundCancellationException.cs b/BitPay/Exceptions/RefundCancellationException.cs new file mode 100644 index 0000000..6710bd8 --- /dev/null +++ b/BitPay/Exceptions/RefundCancellationException.cs @@ -0,0 +1,18 @@ +using System; + +namespace BitPaySDK.Exceptions +{ + public class RefundCancellationException : BitPayException + { + private const string BitPayCode = "BITPAY-REFUND-CANCELLATION"; + private const string BitPayMessage = "Failed to cancel refund"; + + public RefundCancellationException() : base(BitPayCode, BitPayMessage) + { + } + + public RefundCancellationException(Exception ex) : base(BitPayCode, BitPayMessage, ex) + { + } + } +} \ No newline at end of file diff --git a/BitPay/Exceptions/RefundCreationException.cs b/BitPay/Exceptions/RefundCreationException.cs new file mode 100644 index 0000000..8e70f27 --- /dev/null +++ b/BitPay/Exceptions/RefundCreationException.cs @@ -0,0 +1,18 @@ +using System; + +namespace BitPaySDK.Exceptions +{ + public class RefundCreationException : BitPayException + { + private const string BitPayCode = "BITPAY-REFUND-CREATE"; + private const string BitPayMessage = "Failed to create refund"; + + public RefundCreationException() : base(BitPayCode, BitPayMessage) + { + } + + public RefundCreationException(Exception ex) : base(BitPayCode, BitPayMessage, ex) + { + } + } +} \ No newline at end of file diff --git a/BitPay/Exceptions/RefundException.cs b/BitPay/Exceptions/RefundException.cs new file mode 100644 index 0000000..91c729e --- /dev/null +++ b/BitPay/Exceptions/RefundException.cs @@ -0,0 +1,28 @@ +using System; + +namespace BitPaySDK.Exceptions +{ + public class RefundException : BitPayException + { + private const string BitPayMessage = "An unexpected error occured while trying to manage the refund"; + private readonly string _bitpayCode = "BITPAY-REFUND-GENERIC"; + + public RefundException() : base(BitPayMessage) + { + BitpayCode = _bitpayCode; + } + + public RefundException(Exception ex) : base(BitPayMessage, ex) + { + BitpayCode = _bitpayCode; + } + + public RefundException(string bitpayCode, string message) : base(bitpayCode, message) + { + } + + public RefundException(string bitpayCode, string message, Exception cause) : base(bitpayCode, message, cause) + { + } + } +} \ No newline at end of file diff --git a/BitPay/Exceptions/RefundQueryException.cs b/BitPay/Exceptions/RefundQueryException.cs new file mode 100644 index 0000000..b2e4239 --- /dev/null +++ b/BitPay/Exceptions/RefundQueryException.cs @@ -0,0 +1,18 @@ +using System; + +namespace BitPaySDK.Exceptions +{ + public class RefundQueryException : RefundException + { + private const string BitPayCode = "BITPAY-REFUND-GET"; + private const string BitPayMessage = "Failed to retrieve refund"; + + public RefundQueryException() : base(BitPayCode, BitPayMessage) + { + } + + public RefundQueryException(Exception ex) : base(BitPayCode, BitPayMessage, ex) + { + } + } +} \ No newline at end of file diff --git a/BitPay/Models/Invoice/Refund.cs b/BitPay/Models/Invoice/Refund.cs new file mode 100644 index 0000000..531adec --- /dev/null +++ b/BitPay/Models/Invoice/Refund.cs @@ -0,0 +1,63 @@ +using Newtonsoft.Json; + +namespace BitPaySDK.Models.Invoice +{ + public class Refund + { + public Refund() { + } + + // Request fields + // + + [JsonProperty(PropertyName = "guid")] + public string Guid { get; set; } + + [JsonProperty(PropertyName = "refundEmail")] + public string RefundEmail { get; set; } + + [JsonProperty(PropertyName = "amount")] + public double Amount { get; set; } + + [JsonProperty(PropertyName = "token")] + public string Token { get; set; } + + [JsonProperty(PropertyName = "currency")] + public string Currency { get; set; } + + // Response fields + // + + [JsonProperty(PropertyName = "id")] + public string Id { get; set; } + + [JsonProperty(PropertyName = "requestDate")] + public string RequestDate { get; set; } + + [JsonProperty(PropertyName = "status")] + public string Status { get; set; } + + [JsonProperty(PropertyName = "params")] + public RefundParams PaymentUrls { get; set; } + + public bool ShouldSerializeId() + { + return (Id != null); + } + + public bool ShouldSerializeRequestDate() + { + return (RequestDate != null); + } + + public bool ShouldSerializeStatus() + { + return (Status != null); + } + + public bool ShouldSerializePaymentUrls() + { + return (PaymentUrls != null); + } + } +} diff --git a/BitPay/Models/Invoice/RefundParams.cs b/BitPay/Models/Invoice/RefundParams.cs new file mode 100644 index 0000000..3d2664d --- /dev/null +++ b/BitPay/Models/Invoice/RefundParams.cs @@ -0,0 +1,34 @@ +using Newtonsoft.Json; + +namespace BitPaySDK.Models.Invoice +{ + public class RefundParams + { + public RefundParams() { + } + + [JsonProperty(PropertyName = "requesterType")] + public string setRequesterType { get; set; } + + [JsonProperty(PropertyName = "requesterEmail")] + public string setRequesterEmail { get; set; } + + [JsonProperty(PropertyName = "amount")] + public double setAmount { get; set; } + + [JsonProperty(PropertyName = "currency")] + public string setCurrency { get; set; } + + [JsonProperty(PropertyName = "email")] + public string setEmail { get; set; } + + [JsonProperty(PropertyName = "purchaserNotifyEmail")] + public string setPurchaserNotifyEmail { get; set; } + + [JsonProperty(PropertyName = "refundAddress")] + public string setRefundAddress { get; set; } + + [JsonProperty(PropertyName = "supportRequestEid")] + public string setSupportRequestEid { get; set; } + } +} diff --git a/BitPay/Models/Invoice/RefundStatus.cs b/BitPay/Models/Invoice/RefundStatus.cs new file mode 100644 index 0000000..a11958a --- /dev/null +++ b/BitPay/Models/Invoice/RefundStatus.cs @@ -0,0 +1,9 @@ +namespace BitPaySDK.Models.Invoice +{ + public static class RefundStatus + { + public const string Pending = "pending"; + public const string Success = "success"; + public const string Failure = "failure"; + } +} \ No newline at end of file diff --git a/BitPay/Models/Payout/ReceiverInfo.cs b/BitPay/Models/Payout/ReceiverInfo.cs new file mode 100644 index 0000000..177eca1 --- /dev/null +++ b/BitPay/Models/Payout/ReceiverInfo.cs @@ -0,0 +1,35 @@ +using Newtonsoft.Json; + +namespace BitPaySDK.Models.Payout +{ + public class ReceiverInfo + { + + [JsonProperty(PropertyName = "name")] + public string Name { get; set; } + + [JsonProperty(PropertyName = "address1")] + public string Address1 { get; set; } + + [JsonProperty(PropertyName = "address2")] + public string Address2 { get; set; } + + [JsonProperty(PropertyName = "locality")] + public string Locality { get; set; } + + [JsonProperty(PropertyName = "region")] + public string Region { get; set; } + + [JsonProperty(PropertyName = "postalCode")] + public string PostalCode { get; set; } + + [JsonProperty(PropertyName = "country")] + public string Country { get; set; } + + [JsonProperty(PropertyName = "email")] + public string Email { get; set; } + + [JsonProperty(PropertyName = "phone")] + public string Phone { get; set; } + } +} diff --git a/BitPayXUnitTest/BitPayTests.cs b/BitPayXUnitTest/BitPayTests.cs index 4f70778..cb6fd8f 100644 --- a/BitPayXUnitTest/BitPayTests.cs +++ b/BitPayXUnitTest/BitPayTests.cs @@ -285,6 +285,36 @@ public async Task TestShouldGetInvoices() { Assert.True(invoices.Count > 0, "No invoices retrieved"); } + /* + To use this test: + You must have a paid/completed invoice in your account (list of invoices). The test looks for the first invoice in the "complete" + state and authorises a refund. The actual refund will not be executed until the email receiver enters his bitcoin refund address. + */ + [Fact] + public async Task testShouldCreateGetCancelRefundRequest() { + //check within the last few days + var date = DateTime.Now; + var today = date; + var sevenDaysAgo = date.AddDays(-95); + var invoices = await _bitpay.GetInvoices(sevenDaysAgo, today, InvoiceStatus.Complete); + Invoice firstInvoice = invoices.First(); + + Assert.NotNull(firstInvoice); + string refundEmail = ""; + + Boolean createdRefund = await _bitpay.CreateRefund(firstInvoice, refundEmail, firstInvoice.Price, firstInvoice.Currency); + List retrievedRefunds = await _bitpay.GetRefunds(firstInvoice); + Refund firstRefund = retrievedRefunds.First(); + Refund retrievedRefund = await _bitpay.GetRefund(firstInvoice, firstRefund.Id); + Boolean cancelled = await _bitpay.CancelRefund(firstInvoice, firstRefund.Id); + + Assert.True(createdRefund); + Assert.True(retrievedRefunds.Count > 0); + Assert.NotNull(firstRefund); + Assert.NotNull(retrievedRefund); + Assert.True(cancelled); + } + [Fact] public async Task TestShouldGetLedgerBtc() {