public async Task <IActionResult> ClaimPullPayment(string pullPaymentId, ViewPullPaymentModel vm) { using var ctx = _dbContextFactory.CreateContext(); var pp = await ctx.PullPayments.FindAsync(pullPaymentId); if (pp is null) { ModelState.AddModelError(nameof(pullPaymentId), "This pull payment does not exists"); } var ppBlob = pp.GetBlob(); var network = _networkProvider.GetNetwork <BTCPayNetwork>(ppBlob.SupportedPaymentMethods.Single().CryptoCode); var paymentMethodId = ppBlob.SupportedPaymentMethods.Single(); var payoutHandler = _payoutHandlers.FirstOrDefault(handler => handler.CanHandle(paymentMethodId)); IClaimDestination destination = await payoutHandler?.ParseClaimDestination(paymentMethodId, vm.Destination); if (destination is null) { ModelState.AddModelError(nameof(vm.Destination), $"Invalid destination"); } if (!ModelState.IsValid) { return(await ViewPullPayment(pullPaymentId)); } var result = await _pullPaymentHostedService.Claim(new ClaimRequest() { Destination = destination, PullPaymentId = pullPaymentId, Value = vm.ClaimedAmount, PaymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike) }); if (result.Result != ClaimRequest.ClaimResult.Ok) { if (result.Result == ClaimRequest.ClaimResult.AmountTooLow) { ModelState.AddModelError(nameof(vm.ClaimedAmount), ClaimRequest.GetErrorMessage(result.Result)); } else { ModelState.AddModelError(string.Empty, ClaimRequest.GetErrorMessage(result.Result)); } return(await ViewPullPayment(pullPaymentId)); } else { TempData.SetStatusMessageModel(new StatusMessageModel() { Message = $"Your claim request of {_currencyNameTable.DisplayFormatCurrency(vm.ClaimedAmount, ppBlob.Currency)} to {vm.Destination} has been submitted and is awaiting approval.", Severity = StatusMessageModel.StatusSeverity.Success }); } return(RedirectToAction(nameof(ViewPullPayment), new { pullPaymentId = pullPaymentId })); }
public async Task <IActionResult> CreatePayout(string pullPaymentId, CreatePayoutRequest request) { if (request is null) { return(NotFound()); } if (!PaymentMethodId.TryParse(request?.PaymentMethod, out var paymentMethodId)) { ModelState.AddModelError(nameof(request.PaymentMethod), "Invalid payment method"); return(this.CreateValidationError(ModelState)); } var payoutHandler = _payoutHandlers.FirstOrDefault(handler => handler.CanHandle(paymentMethodId)); if (payoutHandler is null) { ModelState.AddModelError(nameof(request.PaymentMethod), "Invalid payment method"); return(this.CreateValidationError(ModelState)); } await using var ctx = _dbContextFactory.CreateContext(); var pp = await ctx.PullPayments.FindAsync(pullPaymentId); if (pp is null) { return(PullPaymentNotFound()); } var ppBlob = pp.GetBlob(); IClaimDestination destination = await payoutHandler.ParseClaimDestination(paymentMethodId, request.Destination); if (destination is null) { ModelState.AddModelError(nameof(request.Destination), "The destination must be an address or a BIP21 URI"); return(this.CreateValidationError(ModelState)); } if (request.Amount is decimal v && (v < ppBlob.MinimumClaim || v == 0.0m)) { ModelState.AddModelError(nameof(request.Amount), $"Amount too small (should be at least {ppBlob.MinimumClaim})"); return(this.CreateValidationError(ModelState)); } var cd = _currencyNameTable.GetCurrencyData(pp.GetBlob().Currency, false); var result = await _pullPaymentService.Claim(new ClaimRequest() { Destination = destination, PullPaymentId = pullPaymentId, Value = request.Amount, PaymentMethodId = paymentMethodId }); switch (result.Result) { case ClaimRequest.ClaimResult.Ok: break; case ClaimRequest.ClaimResult.Duplicate: return(this.CreateAPIError("duplicate-destination", ClaimRequest.GetErrorMessage(result.Result))); case ClaimRequest.ClaimResult.Expired: return(this.CreateAPIError("expired", ClaimRequest.GetErrorMessage(result.Result))); case ClaimRequest.ClaimResult.NotStarted: return(this.CreateAPIError("not-started", ClaimRequest.GetErrorMessage(result.Result))); case ClaimRequest.ClaimResult.Archived: return(this.CreateAPIError("archived", ClaimRequest.GetErrorMessage(result.Result))); case ClaimRequest.ClaimResult.Overdraft: return(this.CreateAPIError("overdraft", ClaimRequest.GetErrorMessage(result.Result))); case ClaimRequest.ClaimResult.AmountTooLow: return(this.CreateAPIError("amount-too-low", ClaimRequest.GetErrorMessage(result.Result))); case ClaimRequest.ClaimResult.PaymentMethodNotSupported: return(this.CreateAPIError("payment-method-not-supported", ClaimRequest.GetErrorMessage(result.Result))); default: throw new NotSupportedException("Unsupported ClaimResult"); } return(Ok(ToModel(result.PayoutData, cd))); }
public async Task <IActionResult> ClaimPullPayment(string pullPaymentId, ViewPullPaymentModel vm) { using var ctx = _dbContextFactory.CreateContext(); var pp = await ctx.PullPayments.FindAsync(pullPaymentId); if (pp is null) { ModelState.AddModelError(nameof(pullPaymentId), "This pull payment does not exists"); } var ppBlob = pp.GetBlob(); var paymentMethodId = ppBlob.SupportedPaymentMethods.FirstOrDefault(id => vm.SelectedPaymentMethod == id.ToString()); var payoutHandler = paymentMethodId is null ? null : _payoutHandlers.FindPayoutHandler(paymentMethodId); if (payoutHandler is null) { ModelState.AddModelError(nameof(vm.SelectedPaymentMethod), "Invalid destination with selected payment method"); return(await ViewPullPayment(pullPaymentId)); } var destination = await payoutHandler.ParseAndValidateClaimDestination(paymentMethodId, vm.Destination, ppBlob); if (destination.destination is null) { ModelState.AddModelError(nameof(vm.Destination), destination.error ?? "Invalid destination with selected payment method"); return(await ViewPullPayment(pullPaymentId)); } if (vm.ClaimedAmount == 0) { ModelState.AddModelError(nameof(vm.ClaimedAmount), "Amount is required"); } else { var amount = ppBlob.Currency == "SATS" ? new Money(vm.ClaimedAmount, MoneyUnit.Satoshi).ToUnit(MoneyUnit.BTC) : vm.ClaimedAmount; if (destination.destination.Amount != null && amount != destination.destination.Amount) { var implied = _currencyNameTable.DisplayFormatCurrency(destination.destination.Amount.Value, paymentMethodId.CryptoCode); var provided = _currencyNameTable.DisplayFormatCurrency(vm.ClaimedAmount, ppBlob.Currency); ModelState.AddModelError(nameof(vm.ClaimedAmount), $"Amount implied in destination ({implied}) does not match the payout amount provided ({provided})."); } } if (!ModelState.IsValid) { return(await ViewPullPayment(pullPaymentId)); } var result = await _pullPaymentHostedService.Claim(new ClaimRequest() { Destination = destination.destination, PullPaymentId = pullPaymentId, Value = vm.ClaimedAmount, PaymentMethodId = paymentMethodId }); if (result.Result != ClaimRequest.ClaimResult.Ok) { if (result.Result == ClaimRequest.ClaimResult.AmountTooLow) { ModelState.AddModelError(nameof(vm.ClaimedAmount), ClaimRequest.GetErrorMessage(result.Result)); } else { ModelState.AddModelError(string.Empty, ClaimRequest.GetErrorMessage(result.Result)); } return(await ViewPullPayment(pullPaymentId)); } TempData.SetStatusMessageModel(new StatusMessageModel { Message = $"Your claim request of {_currencyNameTable.DisplayFormatCurrency(vm.ClaimedAmount, ppBlob.Currency)} to {vm.Destination} has been submitted and is awaiting {(result.PayoutData.State == PayoutState.AwaitingApproval? "approval": "payment")}.", Severity = StatusMessageModel.StatusSeverity.Success }); return(RedirectToAction(nameof(ViewPullPayment), new { pullPaymentId })); }