diff --git a/BitPay/BitPay.cs b/BitPay/BitPay.cs index 4108eb7..a6176b1 100644 --- a/BitPay/BitPay.cs +++ b/BitPay/BitPay.cs @@ -331,12 +331,77 @@ public async Task GetBill(string billId, string facade = Facade.Merchant, } } + /// + /// Update a bill. + /// + /// An invoice object containing the update. + /// The id of the bill to update. + /// A new bill object returned from the server. + public async Task UpdateBill(Bill bill, string billId) + { + try + { + var json = JsonConvert.SerializeObject(bill); + var response = await Put("bills/" + billId, json).ConfigureAwait(false); + var responseString = await ResponseToJsonString(response).ConfigureAwait(false); + var serializerSettings = new JsonSerializerSettings {ObjectCreationHandling = ObjectCreationHandling.Replace}; + JsonConvert.PopulateObject(responseString, bill, serializerSettings); + } + catch (Exception ex) + { + if (!(ex.GetType().IsSubclassOf(typeof(BitPayException)) || ex.GetType() == typeof(BitPayException))) + throw new BillUpdateException(ex); + + throw; + } + + return bill; + } + + /// + /// Retrieve a bill by id. + /// + /// The status to filter the bills. + /// The bill object retrieved from the server. + public async Task> GetBills(string status = null) + { + Dictionary parameters = null; + try + { + // Provide the merchant token when the merchant facade is being used. + // GET/invoices expects the merchant token and not the merchant/invoice token. + try + { + parameters = new Dictionary{}; + parameters.Add("token", GetAccessToken(Facade.Merchant)); + if (!String.IsNullOrEmpty(status)) + { + parameters.Add("status", status); + } + } + catch (BitPayException) + { + // No token for invoice. + parameters = null; + } + var response = await Get("bills", 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 BillQueryException(ex); + + throw; + } + } + /// /// Deliver a bill to the consumer. /// /// The id of the requested bill. /// The token of the requested bill. - /// The facade to deliver the bill from /// Allow unsigned request /// A response status returned from the API. public async Task DeliverBill(string billId, string billToken, bool signRequest = true) @@ -423,14 +488,12 @@ public async Task SubmitPayoutBatch(PayoutBatch batch) batch.Guid = Guid.NewGuid().ToString(); var json = JsonConvert.SerializeObject(batch); var response = await PostWithSignature("payouts", json); - - // To avoid having to merge instructions in the response with those we sent, we just remove the instructions - // we sent and replace with those in the response. - batch.Instructions = new List(); + var responseString = await ResponseToJsonString(response); JsonConvert.PopulateObject(responseString, batch, new JsonSerializerSettings { - NullValueHandling = NullValueHandling.Ignore + NullValueHandling = NullValueHandling.Ignore, + ObjectCreationHandling = ObjectCreationHandling.Replace }); // Track the token for this batch @@ -450,12 +513,17 @@ public async Task SubmitPayoutBatch(PayoutBatch batch) /// /// Retrieve a collection of BitPay payout batches. /// + /// The status to filter (optional). /// A list of BitPay PayoutBatch objects. - public async Task> GetPayoutBatches() + public async Task> GetPayoutBatches(string status = null) { try { var parameters = InitParams(); + if (!string.IsNullOrEmpty(status)) + { + parameters.Add("status", status); + } parameters.Add("token", GetAccessToken(Facade.Payroll)); var response = await Get("payouts", parameters); var responseString = await ResponseToJsonString(response); @@ -891,6 +959,29 @@ private async Task Post(string uri, string json, bool signa } } + private async Task Put(string uri, string json) + { + try + { + var bodyContent = new StringContent(UnicodeToAscii(json)); + _httpClient.DefaultRequestHeaders.Clear(); + _httpClient.DefaultRequestHeaders.Add("x-accept-version", BitpayApiVersion); + _httpClient.DefaultRequestHeaders.Add("x-bitpay-plugin-info", BitpayPluginInfo); + bodyContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + + var signature = KeyUtils.Sign(_ecKey, _baseUrl + uri + json); + _httpClient.DefaultRequestHeaders.Add("x-signature", signature); + _httpClient.DefaultRequestHeaders.Add("x-identity", _ecKey?.PublicKeyHexBytes); + + var result = await _httpClient.PutAsync(uri, bodyContent).ConfigureAwait(false); + return result; + } + catch (Exception ex) + { + throw new BitPayApiCommunicationException(ex); + } + } + private async Task ResponseToJsonString(HttpResponseMessage response) { if (response == null) diff --git a/BitPay/BitPay.csproj b/BitPay/BitPay.csproj index 872ac80..973b3a7 100644 --- a/BitPay/BitPay.csproj +++ b/BitPay/BitPay.csproj @@ -266,6 +266,7 @@ + @@ -307,10 +308,10 @@ - + @@ -319,6 +320,7 @@ + diff --git a/BitPay/Exceptions/BillUpdateException.cs b/BitPay/Exceptions/BillUpdateException.cs new file mode 100644 index 0000000..b92865b --- /dev/null +++ b/BitPay/Exceptions/BillUpdateException.cs @@ -0,0 +1,18 @@ +using System; + +namespace BitPayAPI.Exceptions +{ + public class BillUpdateException : BitPayException + { + private const string BitPayCode = "BITPAY-BILL-UPDATE"; + private const string BitPayMessage = "Failed to update bill"; + + public BillUpdateException() : base(BitPayCode, BitPayMessage) + { + } + + public BillUpdateException(Exception ex) : base(BitPayCode, BitPayMessage, ex) + { + } + } +} \ No newline at end of file diff --git a/BitPay/Models/Payout/PayoutBatch.cs b/BitPay/Models/Payout/PayoutBatch.cs index 476250d..64a78fe 100644 --- a/BitPay/Models/Payout/PayoutBatch.cs +++ b/BitPay/Models/Payout/PayoutBatch.cs @@ -7,13 +7,6 @@ namespace BitPayAPI.Models.Payout { public class PayoutBatch { - public const string StatusNew = "new"; - public const string StatusFunded = "funded"; - public const string StatusProcessing = "processing"; - public const string StatusComplete = "complete"; - public const string StatusFailed = "failed"; - public const string StatusCancelled = "cancelled"; - public const string MethodManual2 = "manual_2"; public const string MethodVwap24 = "vwap_24hr"; @@ -26,8 +19,6 @@ public PayoutBatch() { Amount = 0.0; Currency = "USD"; - Reference = ""; - BankTransferId = ""; NotificationEmail = ""; NotificationUrl = ""; PricingMethod = MethodVwap24; @@ -36,21 +27,17 @@ public PayoutBatch() /// /// Constructor, create an instruction-full request PayoutBatch object. /// + /// Currency to use on payout. /// /// Date when request is effective. Note that the time of day will automatically be set to /// 09:00:00.000 UTC time for the given day. Only requests submitted before 09:00:00.000 UTC are guaranteed to be /// processed on the same day. /// - /// Merchant-provided data. - /// Merchant-provided data, to help match funding payments to payout batches. /// Payout instructions. - public PayoutBatch(string currency, DateTime effectiveDate, string bankTransferId, string reference, - List instructions) : this() + public PayoutBatch(string currency, DateTime effectiveDate, List instructions) : this() { Currency = currency; EffectiveDate = effectiveDate; - BankTransferId = bankTransferId; - Reference = reference; Instructions = instructions; _computeAndSetAmount(); } @@ -65,22 +52,6 @@ public PayoutBatch(string currency, DateTime effectiveDate, string bankTransferI // Required fields // - [JsonProperty(PropertyName = "effectiveDate")] - [JsonConverter(typeof(Converters.DateStringConverter))] - public DateTime EffectiveDate { get; set; } - - [JsonProperty(PropertyName = "reference")] - public string Reference { get; set; } - - [JsonProperty(PropertyName = "bankTransferId")] - public string BankTransferId { get; set; } - - // Optional fields - // - - [JsonProperty(PropertyName = "instructions")] - public List Instructions { get; set; } - [JsonProperty(PropertyName = "amount")] public double Amount { get; set; } @@ -90,14 +61,25 @@ public string Currency get => _currency; set { - if (value.Length != 3) - throw new BitPayException("Error: currency code must be exactly three characters"); + if (!Models.Currency.isValid(value)) + throw new BitPayException("Error: currency code must be a type of BitPayAPI.Models.Currency"); + _currency = value; } } - [JsonProperty(PropertyName = "pricingMethod")] - public string PricingMethod { get; set; } + [JsonProperty(PropertyName = "effectiveDate")] + [JsonConverter(typeof(Converters.DateStringConverter))] + public DateTime EffectiveDate { get; set; } + + [JsonProperty(PropertyName = "instructions")] + public List Instructions { get; set; } + + // Optional fields + // + + [JsonProperty(PropertyName = "reference")] + public string Reference { get; set; } [JsonProperty(PropertyName = "notificationEmail")] public string NotificationEmail { get; set; } @@ -105,6 +87,9 @@ public string Currency [JsonProperty(PropertyName = "notificationURL")] public string NotificationUrl { get; set; } + [JsonProperty(PropertyName = "pricingMethod")] + public string PricingMethod { get; set; } + // Response fields // @@ -112,12 +97,9 @@ public string Currency public string Account { get; set; } - public string Status { get; set; } - - public double Btc { get; set; } + public string SupportPhone { get; set; } - [JsonConverter(typeof(Converters.DateStringConverter))] - public DateTime RequestDate { get; set; } + public string Status { get; set; } public double PercentFee { get; set; } @@ -125,7 +107,15 @@ public string Currency public double DepositTotal { get; set; } - public string SupportPhone { get; set; } + public double Rate { get; set; } + + public double Btc { get; set; } + + [JsonConverter(typeof(Converters.DateStringConverter))] + public DateTime RequestDate { get; set; } + + [JsonConverter(typeof(Converters.DateStringConverter))] + public DateTime DateExecuted { get; set; } // Private methods // @@ -177,6 +167,11 @@ public bool ShouldSerializeStatus() return false; } + public bool ShouldSerializeRate() + { + return false; + } + public bool ShouldSerializeBtc() { return false; @@ -206,5 +201,15 @@ public bool ShouldSerializeSupportPhone() { return false; } + + public bool ShouldSerializeReference() + { + return !string.IsNullOrEmpty(Reference); + } + + public bool ShouldSerializeDateExecuted() + { + return false; + } } } \ No newline at end of file diff --git a/BitPay/Models/Payout/PayoutInstruction.cs b/BitPay/Models/Payout/PayoutInstruction.cs index fb3d61a..951b909 100644 --- a/BitPay/Models/Payout/PayoutInstruction.cs +++ b/BitPay/Models/Payout/PayoutInstruction.cs @@ -5,9 +5,6 @@ namespace BitPayAPI.Models.Payout { public class PayoutInstruction { - public const string StatusPaid = "paid"; - public const string StatusUnpaid = "unpaid"; - /// /// Constructor, create an empty PayoutInstruction object. /// @@ -20,12 +17,10 @@ public PayoutInstruction() /// /// BTC amount. /// Bitcoin address. - /// Label. - public PayoutInstruction(double amount, string address, string label) + public PayoutInstruction(double amount, string address) { Amount = amount; Address = address; - Label = label; } [JsonProperty(PropertyName = "amount")] @@ -34,50 +29,51 @@ public PayoutInstruction(double amount, string address, string label) [JsonProperty(PropertyName = "address")] public string Address { get; set; } - [JsonProperty(PropertyName = "label")] public string Label { get; set; } + [JsonProperty(PropertyName = "label")] + public string Label { get; set; } // Response fields // public string Id { get; set; } - public string Status { get; set; } - public PayoutInstructionBtcSummary Btc { get; set; } public List Transactions { get; set; } - public bool ShouldSerializeAmount() + public string Status { get; set; } + + public bool ShouldSerializeId() { - return true; + return false; } - public bool ShouldSerializeAddress() + public bool ShouldSerializeAmount() { - return !string.IsNullOrEmpty(Address); + return true; } - public bool ShouldSerializeLabel() + public bool ShouldSerializeBtc() { - return !string.IsNullOrEmpty(Label); + return false; } - public bool ShouldSerializeId() + public bool ShouldSerializeAddress() { - return false; + return !string.IsNullOrEmpty(Address); } - public bool ShouldSerializeStatus() + public bool ShouldSerializeTransactions() { return false; } - public bool ShouldSerializeBtc() + public bool ShouldSerializeLabel() { - return false; + return !string.IsNullOrEmpty(Label); } - public bool ShouldSerializeTransactions() + public bool ShouldSerializeStatus() { return false; } diff --git a/BitPay/Models/Payout/PayoutInstructionTransaction.cs b/BitPay/Models/Payout/PayoutInstructionTransaction.cs index 33dec9c..d119e39 100644 --- a/BitPay/Models/Payout/PayoutInstructionTransaction.cs +++ b/BitPay/Models/Payout/PayoutInstructionTransaction.cs @@ -1,6 +1,11 @@ -namespace BitPayAPI.Models.Payout +using System; + +namespace BitPayAPI.Models.Payout { public class PayoutInstructionTransaction { + public string Txid { get; set; } + public double Amount { get; set; } + public DateTime Date { get; set; } } } \ No newline at end of file diff --git a/BitPay/Models/Payout/Status.cs b/BitPay/Models/Payout/Status.cs new file mode 100644 index 0000000..398ccb0 --- /dev/null +++ b/BitPay/Models/Payout/Status.cs @@ -0,0 +1,14 @@ +namespace BitPayAPI.Models.Payout +{ + public static class Status + { + public const string New = "new"; + public const string Funded = "funded"; + public const string Processing = "processing"; + public const string Complete = "complete"; + public const string Failed = "failed"; + public const string Cancelled = "cancelled"; + public const string Paid = "paid"; + public const string Unpaid = "unpaid"; + } +} \ No newline at end of file diff --git a/BitPay/Models/Payout/PayoutInfo.cs b/BitPay/Models/Settlement/PayoutInfo.cs similarity index 88% rename from BitPay/Models/Payout/PayoutInfo.cs rename to BitPay/Models/Settlement/PayoutInfo.cs index 84b0b76..3a79f8d 100644 --- a/BitPay/Models/Payout/PayoutInfo.cs +++ b/BitPay/Models/Settlement/PayoutInfo.cs @@ -1,4 +1,4 @@ -namespace BitPayAPI.Models.Payout +namespace BitPayAPI.Models.Settlement { public class PayoutInfo { diff --git a/BitPay/Models/Settlement/Settlement.cs b/BitPay/Models/Settlement/Settlement.cs index ceb73fb..9787ce7 100644 --- a/BitPay/Models/Settlement/Settlement.cs +++ b/BitPay/Models/Settlement/Settlement.cs @@ -8,7 +8,7 @@ public class Settlement public string Id { get; set; } public string AccountId { get; set; } public string Currency { get; set; } - public Payout.PayoutInfo PayoutInfo { get; set; } + public PayoutInfo PayoutInfo { get; set; } public string Status { get; set; } public DateTime DateCreated { get; set; } public DateTime DateExecuted { get; set; } diff --git a/BitPayUnitTest/BitPayTest.cs b/BitPayUnitTest/BitPayTest.cs index c643222..13129b2 100644 --- a/BitPayUnitTest/BitPayTest.cs +++ b/BitPayUnitTest/BitPayTest.cs @@ -14,6 +14,7 @@ using Buyer = BitPayAPI.Models.Invoice.Buyer; using InvoiceStatus = BitPayAPI.Models.Invoice.Status; using BillStatus = BitPayAPI.Models.Bill.Status; +using PayoutStatus = BitPayAPI.Models.Payout.Status; namespace BitPayUnitTest { @@ -147,14 +148,6 @@ public async Task TestShouldGetInvoice() { var retrievedInvoice = await _bitpay.GetInvoice(invoice.Id); Assert.AreEqual(invoice.Id, retrievedInvoice.Id, "Expected invoice not retrieved"); } - - [TestMethod] - public async Task TestShouldGetInvoiceNoSigned() { - // create an invoice without signature then retrieve it through the get method - they should match - var invoice = await _bitpay.CreateInvoice(new Invoice(100.0, Currency.EUR), signRequest: false); - var retrievedInvoice = await _bitpay.GetInvoice(invoice.Id, Facade.PointOfSale, false); - Assert.AreEqual(invoice.Id, retrievedInvoice.Id); - } [TestMethod] public async Task TestShouldCreateInvoiceWithAdditionalParams() { @@ -282,15 +275,13 @@ public async Task TestShouldSubmitPayoutBatch() { var threeDaysFromNow = date.AddDays(3); var effectiveDate = threeDaysFromNow; - var reference = "My test batch"; - var bankTransferId = "My bank transfer id"; var currency = Currency.USD; var instructions = new List() { - new PayoutInstruction(100.0, "mtHDtQtkEkRRB5mgeWpLhALsSbga3iZV6u", "Alice"), - new PayoutInstruction(200.0, "mvR4Xj7MYT7GJcL93xAQbSZ2p4eHJV5F7A", "Bob") + new PayoutInstruction(100.0, "mtHDtQtkEkRRB5mgeWpLhALsSbga3iZV6u"), + new PayoutInstruction(200.0, "mvR4Xj7MYT7GJcL93xAQbSZ2p4eHJV5F7A") }; - var batch = new PayoutBatch(currency, effectiveDate, bankTransferId, reference, instructions); + var batch = new PayoutBatch(currency, effectiveDate, instructions); batch = await _bitpay.SubmitPayoutBatch(batch); Assert.IsNotNull(batch.Id, "Batch created with id=NULL"); @@ -300,24 +291,17 @@ public async Task TestShouldSubmitPayoutBatch() { [TestMethod] public async Task TestShouldSubmitGetAndDeletePayoutBatch() { - /* - Unfortunately at the time of this writing the Payroll facade is not available through the API - so this test will always fail - since you can't approve the Payroll pairing code - */ - var date = DateTime.Now; var threeDaysFromNow = date.AddDays(3); var effectiveDate = threeDaysFromNow; - var reference = "My test batch"; - var bankTransferId = "My bank transfer id"; var currency = Currency.USD; var instructions = new List() { - new PayoutInstruction(100.0, "mtHDtQtkEkRRB5mgeWpLhALsSbga3iZV6u", "Alice"), - new PayoutInstruction(200.0, "mvR4Xj7MYT7GJcL93xAQbSZ2p4eHJV5F7A", "Bob") + new PayoutInstruction(100.0, "mtHDtQtkEkRRB5mgeWpLhALsSbga3iZV6u"), + new PayoutInstruction(200.0, "mvR4Xj7MYT7GJcL93xAQbSZ2p4eHJV5F7A") }; - var batch0 = new PayoutBatch(currency, effectiveDate, bankTransferId, reference, instructions); + var batch0 = new PayoutBatch(currency, effectiveDate, instructions); batch0 = await _bitpay.SubmitPayoutBatch(batch0); Assert.IsNotNull(batch0.Id, "Batch (0) created with id=NULL"); @@ -332,6 +316,20 @@ so this test will always fail - since you can't approve the Payroll pairing code } + [TestMethod] + public async Task TestShouldGetPayoutBatches() { + + var batches = await _bitpay.GetPayoutBatches(); + Assert.IsTrue(batches.Count > 0, "No batches retrieved"); + } + + [TestMethod] + public async Task TestShouldGetPayoutBatchesByStatus() { + + var batches = await _bitpay.GetPayoutBatches(PayoutStatus.New); + Assert.IsTrue(batches.Count > 0, "No batches retrieved"); + } + [TestMethod] public async Task TestGetSettlements() { @@ -471,7 +469,36 @@ public async Task TestShouldGetBill() { }; var basicBill = await _bitpay.CreateBill(bill); var retrievedBill = await _bitpay.GetBill(basicBill.Id); - Assert.AreEqual(bill.Id, retrievedBill.Id); + Assert.AreEqual(basicBill.Id, retrievedBill.Id); + } + + [TestMethod] + public async Task TestShouldGetAndUpdateBill() { + List items = new List(); + items.Add(new Item(){Price = 30.0, Quantity = 9, Description = "product-a"}); + items.Add(new Item(){Price = 14.0, Quantity = 16, Description = "product-b"}); + items.Add(new Item(){Price = 3.90, Quantity = 42, Description = "product-c"}); + items.Add(new Item(){Price = 6.99, Quantity = 12, Description = "product-d"}); + + var bill = new Bill() + { + Number = "6", + Currency = Currency.USD, + Email = "agallardo@bitpay.com", + Items = items, + Name = "basicBill" + }; + var basicBill = await _bitpay.CreateBill(bill); + var retrievedBill = await _bitpay.GetBill(basicBill.Id); + retrievedBill.Currency = Currency.EUR; + retrievedBill.Name = "updatedBill"; + retrievedBill.Items.Add(new Item(){Price = 60.0, Quantity = 7, Description = "product-added"}); + + var updatedBill = await _bitpay.UpdateBill(retrievedBill, retrievedBill.Id); + Assert.Equals(basicBill.Id, retrievedBill.Id); + Assert.Equals(retrievedBill.Id, updatedBill.Id); + Assert.Equals(updatedBill.Currency, Currency.EUR); + Assert.Equals(updatedBill.Name, "updatedBill"); } [TestMethod] @@ -500,5 +527,19 @@ public async Task TestShouldDeliverBill() // Confirm that the bill is sent Assert.AreEqual(BillStatus.Sent, retrievedBill.Status); } + + [TestMethod] + public async Task TestShouldGetBills() { + + var bills = await _bitpay.GetBills(); + Assert.IsTrue(bills.Count > 0, "No bills retrieved"); + } + + [TestMethod] + public async Task TestShouldGetBillsByStatus() { + + var bills = await _bitpay.GetBills(BillStatus.Sent); + Assert.IsTrue(bills.Count > 0, "No bills retrieved"); + } } } diff --git a/BitPayXUnitTest/BitPayTests.cs b/BitPayXUnitTest/BitPayTests.cs index 82cf4c6..1f43aba 100644 --- a/BitPayXUnitTest/BitPayTests.cs +++ b/BitPayXUnitTest/BitPayTests.cs @@ -14,6 +14,7 @@ using Buyer = BitPayAPI.Models.Invoice.Buyer; using InvoiceStatus = BitPayAPI.Models.Invoice.Status; using BillStatus = BitPayAPI.Models.Bill.Status; +using PayoutStatus = BitPayAPI.Models.Payout.Status; namespace BitPayXUnitTest { @@ -146,14 +147,6 @@ public async Task TestShouldGetInvoice() { var retrievedInvoice = await _bitpay.GetInvoice(invoice.Id); Assert.Equal(invoice.Id, retrievedInvoice.Id); } - - [Fact] - public async Task TestShouldGetInvoiceNoSigned() { - // create an invoice then retrieve it through the get method - they should match - var invoice = await _bitpay.CreateInvoice(new Invoice(100.0, Currency.EUR), signRequest: false); - var retrievedInvoice = await _bitpay.GetInvoice(invoice.Id, Facade.PointOfSale, false); - Assert.Equal(invoice.Id, retrievedInvoice.Id); - } [Fact] public async Task TestShouldCreateInvoiceWithAdditionalParams() { @@ -274,15 +267,13 @@ public async Task TestShouldSubmitPayoutBatch() { var threeDaysFromNow = date.AddDays(3); var effectiveDate = threeDaysFromNow; - var reference = "My test batch"; - var bankTransferId = "My bank transfer id"; var currency = Currency.USD; var instructions = new List() { - new PayoutInstruction(100.0, "mtHDtQtkEkRRB5mgeWpLhALsSbga3iZV6u", "Alice"), - new PayoutInstruction(200.0, "mvR4Xj7MYT7GJcL93xAQbSZ2p4eHJV5F7A", "Bob") + new PayoutInstruction(100.0, "mtHDtQtkEkRRB5mgeWpLhALsSbga3iZV6u"), + new PayoutInstruction(200.0, "mvR4Xj7MYT7GJcL93xAQbSZ2p4eHJV5F7A") }; - var batch = new PayoutBatch(currency, effectiveDate, bankTransferId, reference, instructions); + var batch = new PayoutBatch(currency, effectiveDate, instructions); batch = await _bitpay.SubmitPayoutBatch(batch); Assert.NotNull(batch.Id); @@ -291,25 +282,18 @@ public async Task TestShouldSubmitPayoutBatch() { [Fact] public async Task TestShouldSubmitGetAndDeletePayoutBatch() { - - /* - Unfortunately at the time of this writing the Payroll facade is not available through the API - so this test will always fail - since you can't approve the Payroll pairing code - */ - + var date = DateTime.Now; var threeDaysFromNow = date.AddDays(3); var effectiveDate = threeDaysFromNow; - var reference = "My test batch"; - var bankTransferId = "My bank transfer id"; var currency = Currency.USD; var instructions = new List() { - new PayoutInstruction(100.0, "mtHDtQtkEkRRB5mgeWpLhALsSbga3iZV6u", "Alice"), - new PayoutInstruction(200.0, "mvR4Xj7MYT7GJcL93xAQbSZ2p4eHJV5F7A", "Bob") + new PayoutInstruction(100.0, "mtHDtQtkEkRRB5mgeWpLhALsSbga3iZV6u"), + new PayoutInstruction(200.0, "mvR4Xj7MYT7GJcL93xAQbSZ2p4eHJV5F7A") }; - var batch0 = new PayoutBatch(currency, effectiveDate, bankTransferId, reference, instructions); + var batch0 = new PayoutBatch(currency, effectiveDate, instructions); batch0 = await _bitpay.SubmitPayoutBatch(batch0); Assert.NotNull(batch0.Id); @@ -324,6 +308,20 @@ so this test will always fail - since you can't approve the Payroll pairing code } + [Fact] + public async Task TestShouldGetPayoutBatches() { + + var batches = await _bitpay.GetPayoutBatches(); + Assert.True(batches.Count > 0, "No batches retrieved"); + } + + [Fact] + public async Task TestShouldGetPayoutBatchesByStatus() { + + var batches = await _bitpay.GetPayoutBatches(PayoutStatus.New); + Assert.True(batches.Count > 0, "No batches retrieved"); + } + [Fact] public async Task TestGetSettlements() { @@ -463,7 +461,36 @@ public async Task TestShouldGetBill() { }; var basicBill = await _bitpay.CreateBill(bill); var retrievedBill = await _bitpay.GetBill(basicBill.Id); - Assert.Equal(bill.Id, retrievedBill.Id); + Assert.Equal(basicBill.Id, retrievedBill.Id); + } + + [Fact] + public async Task TestShouldGetAndUpdateBill() { + List items = new List(); + items.Add(new Item(){Price = 30.0, Quantity = 9, Description = "product-a"}); + items.Add(new Item(){Price = 14.0, Quantity = 16, Description = "product-b"}); + items.Add(new Item(){Price = 3.90, Quantity = 42, Description = "product-c"}); + items.Add(new Item(){Price = 6.99, Quantity = 12, Description = "product-d"}); + + var bill = new Bill() + { + Number = "6", + Currency = Currency.USD, + Email = "agallardo@bitpay.com", + Items = items, + Name = "basicBill" + }; + var basicBill = await _bitpay.CreateBill(bill); + var retrievedBill = await _bitpay.GetBill(basicBill.Id); + retrievedBill.Currency = Currency.EUR; + retrievedBill.Name = "updatedBill"; + retrievedBill.Items.Add(new Item(){Price = 60.0, Quantity = 7, Description = "product-added"}); + + var updatedBill = await _bitpay.UpdateBill(retrievedBill, retrievedBill.Id); + Assert.Equal(basicBill.Id, retrievedBill.Id); + Assert.Equal(retrievedBill.Id, updatedBill.Id); + Assert.Equal(updatedBill.Currency, Currency.EUR); + Assert.Equal(updatedBill.Name, "updatedBill"); } [Fact] @@ -493,5 +520,18 @@ public async Task TestShouldDeliverBill() Assert.Equal(BillStatus.Sent, retrievedBill.Status); } + [Fact] + public async Task TestShouldGetBills() { + + var bills = await _bitpay.GetBills(); + Assert.True(bills.Count > 0, "No bills retrieved"); + } + + [Fact] + public async Task TestShouldGetBillsByStatus() { + + var bills = await _bitpay.GetBills(BillStatus.Sent); + Assert.True(bills.Count > 0, "No bills retrieved"); + } } } \ No newline at end of file