Modal invoice through btcpay.js (#381)

* Modal through btcpay.js

* Handling close action depending on whether is modal or not

* Tweaking button position

* Stripping trailing slashes if present when setting site root
This commit is contained in:
Rockstar Developer
2018-11-09 01:09:09 -06:00
committed by Nicolas Dorier
parent c9c7316b7d
commit aa1ac3da50
8 changed files with 205 additions and 70 deletions

View File

@@ -174,7 +174,8 @@ namespace BTCPayServer.Controllers
[AcceptMediaTypeConstraint("application/bitcoin-paymentrequest", false)]
[XFrameOptionsAttribute(null)]
[ReferrerPolicyAttribute("origin")]
public async Task<IActionResult> Checkout(string invoiceId, string id = null, string paymentMethodId = null)
public async Task<IActionResult> Checkout(string invoiceId, string id = null, string paymentMethodId = null,
[FromQuery]string view = null)
{
//Keep compatibility with Bitpay
invoiceId = invoiceId ?? id;
@@ -185,6 +186,8 @@ namespace BTCPayServer.Controllers
if (model == null)
return NotFound();
if (view == "modal")
model.IsModal = true;
_CSP.Add(new ConsentSecurityPolicy("script-src", "'unsafe-eval'")); // Needed by Vue
if (!string.IsNullOrEmpty(model.CustomCSSLink) &&

View File

@@ -21,6 +21,7 @@ namespace BTCPayServer.Models.InvoicingModels
public string CustomLogoLink { get; set; }
public string DefaultLang { get; set; }
public List<AvailableCrypto> AvailableCryptos { get; set; } = new List<AvailableCrypto>();
public bool IsModal { get; set; }
public bool IsLightning { get; set; }
public string CryptoCode { get; set; }
public string ServerUrl { get; set; }

View File

@@ -12,6 +12,9 @@
<img class="header__icon__img" src="~/img/logo-white.png" height="40">
}
</div>
<div class="close-icon close-action">
&#10006;
</div>
</div>
<div class="timer-row">
<div class="timer-row__progress-bar" style="width: 0%;"></div>
@@ -326,9 +329,12 @@
</div>
</div>
<div class="success-message">{{$t("This invoice has been paid")}}</div>
<a class="action-button" :href="srvModel.merchantRefLink" v-show="srvModel.merchantRefLink">
<a class="action-button" :href="srvModel.merchantRefLink" v-show="srvModel.merchantRefLink && !isModal">
<span>{{$t("Return to StoreName", srvModel)}}</span>
</a>
<button class="action-button close-action" v-show="isModal">
<span>{{$t("Return to StoreName", srvModel)}}</span>
</button>
</div>
</div>
<div class="button-wrapper refund-address-form-container" id="refund-overpayment-button">
@@ -351,7 +357,7 @@
<div class="bp-view expired" id="expired">
<div>
<div class="expired__body">
<div class="expired__body" style="margin-bottom: 20px;">
<div class="expired__header">{{$t("What happened?")}}</div>
<div class="expired__text" i18n="">
{{$t("InvoiceExpired_Body_1", {storeName: srvModel.storeName, maxTimeMinutes: @Model.MaxTimeMinutes})}}
@@ -370,10 +376,12 @@
{{srvModel.orderId}}
</div>
</div>
<a class="action-button" :href="srvModel.merchantRefLink" v-show="srvModel.merchantRefLink"
style="margin-top: 20px;">
<a class="action-button" :href="srvModel.merchantRefLink" v-show="srvModel.merchantRefLink && !isModal">
<span>{{$t("Return to StoreName", srvModel)}}</span>
</a>
<button class="action-button close-action" v-show="isModal">
<span>{{$t("Return to StoreName", srvModel)}}</span>
</button>
</div>
</div>
</div>

View File

@@ -27,10 +27,20 @@
<link href="@Model.CustomCSSLink" rel="stylesheet" />
}
@if (Model.IsModal)
{
<style type="text/css">
body {
background: rgba(55, 58, 60, 0.4);
}
.close-icon {
display: flex;
}
</style>
}
</head>
<body style="background: #E4E4E4">
<body>
<noscript>
<center style="padding: 2em">
<h2>Javascript is currently disabled in your browser.</h2>
@@ -103,6 +113,7 @@
</div>
</div>
</div>
</div>
</invoice>
<script type="text/javascript">
var storeDefaultLang = '@Model.DefaultLang';
@@ -165,7 +176,8 @@
srvModel: srvModel,
lndModel: null,
scanDisplayQr: "",
expiringSoon: false
expiringSoon: false,
isModal: '@(Model.IsModal ? "true" : "false")'
}
});
</script>

View File

@@ -6,10 +6,6 @@ html {
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
}
article,
aside,
details,
@@ -333,8 +329,9 @@ body {
font-family: "Roboto", "Helvetica", sans-serif;
font-size: 1rem;
line-height: 1.5;
color: #373a3c;
background-color: #fff;
margin: 0;
background: #E4E4E4;
}
[tabindex="-1"]:focus {
@@ -1296,10 +1293,6 @@ html {
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
}
article,
aside,
details,
@@ -1619,14 +1612,6 @@ html {
-webkit-tap-highlight-color: transparent;
}
body {
font-family: "Roboto", "Helvetica", sans-serif;
font-size: 1rem;
line-height: 1.5;
color: #373a3c;
background-color: #fff;
}
[tabindex="-1"]:focus {
outline: none !important;
}
@@ -9084,11 +9069,12 @@ strong {
.close-icon {
flex-grow: 1;
display: flex;
display: none;
justify-content: flex-end;
padding: 13px;
transition: opacity 250ms ease;
opacity: 1;
cursor: pointer;
}
.close-icon img {
@@ -10373,7 +10359,7 @@ All mobile class names should be prefixed by m- */
}
.paid:not(.paid-over) .status-icon {
padding-top: 54px;
padding-top: 45px;
}
.paid:not(.paid-over) .button-wrapper {

View File

@@ -32,7 +32,6 @@ function changeCurrency(currency) {
}
function onDataCallback(jsonData) {
var newStatus = jsonData.status;
if (newStatus === "complete" ||
@@ -123,20 +122,6 @@ $(document).ready(function () {
// initialize
onDataCallback(srvModel);
/* TAF
- Version mobile
- Réparer le décallage par timer
- Preparer les variables de l'API
- Gestion des differents evenements en fonction du status de l'invoice
- sécuriser les CDN
*/
// check if the Document expired
if (srvModel.expirationSeconds > 0) {
progressStart(srvModel.maxTimeSeconds); // Progress bar
@@ -147,7 +132,19 @@ $(document).ready(function () {
hideEmailForm();
}
$(".close-action").on("click", function () {
$("invoice").fadeOut(300, function () {
window.parent.postMessage("close", "*");
});
});
window.parent.postMessage("loaded", "*");
jQuery("invoice").fadeOut(0);
jQuery("invoice").fadeIn(300);
// eof initialize
// FUNCTIONS
function hideEmailForm() {
$("#emailAddressView").removeClass("active");
$("placeholder-refundEmail").html(srvModel.customerEmail);

View File

@@ -0,0 +1,117 @@
/* jshint browser: true, strict: false, maxlen: false, maxstatements: false */
(function () {
function warn() {
if (window.console && window.console.warn) {
window.console.warn.apply(window.console, arguments);
}
}
if (window.btcpay) {
warn('btcpay.js attempted to initialize more than once.');
return;
}
var iframe = document.createElement('iframe');
iframe.name = 'btcpay';
iframe.class = 'btcpay';
iframe.setAttribute('allowtransparency', 'true');
iframe.style.display = 'none';
iframe.style.border = 0;
iframe.style.position = 'fixed';
iframe.style.top = 0;
iframe.style.left = 0;
iframe.style.height = '100%';
iframe.style.width = '100%';
iframe.style.zIndex = '1000';
var origin = 'http://slack.btcpayserver.org join us there, and initialize this with your origin url through setApiUrlPrefix';
// urlPrefix should be site root without trailing slash
function setApiUrlPrefix(urlPrefix) {
origin = stripTrailingSlashes(urlPrefix);
}
function stripTrailingSlashes(site) {
return site.replace(/\/+$/, "");
}
var onModalWillEnterMethod = function () { };
var onModalWillLeaveMethod = function () { };
function showFrame() {
if (window.document.getElementsByName('btcpay').length === 0) {
window.document.body.appendChild(iframe);
}
onModalWillEnterMethod();
iframe.style.display = 'block';
}
function hideFrame() {
onModalWillLeaveMethod();
iframe.style.display = 'none';
iframe = window.document.body.removeChild(iframe);
}
function onModalWillEnter(customOnModalWillEnter) {
onModalWillEnterMethod = customOnModalWillEnter;
}
function onModalWillLeave(customOnModalWillLeave) {
onModalWillLeaveMethod = customOnModalWillLeave;
}
function receiveMessage(event) {
var uri;
if (origin !== event.origin) {
return;
}
if (event.data === 'close') {
hideFrame();
} else if (event.data === 'loaded') {
showFrame();
} else if (event.data && event.data.open) {
uri = event.data.open;
if (uri.indexOf('bitcoin:') === 0) {
window.location = uri;
}
} else if (event.data && event.data.mailto) {
uri = event.data.mailto;
if (uri.indexOf('mailto:') === 0) {
window.location = uri;
}
}
}
function showInvoice(invoiceId, params) {
window.document.body.appendChild(iframe);
var invoiceUrl = origin + '/invoice?id=' + invoiceId + '&view=modal';
if (params && params.animateEntrance === false) {
invoiceUrl += '&animateEntrance=false';
}
iframe.src = invoiceUrl;
}
function setButtonListeners() {
var buttons = window.document.querySelectorAll('[data-btcpay-button]');
for (var i = 0; i < buttons.length; i++) {
var b = buttons[0];
b.addEventListener('submit', showFrame);
}
}
window.addEventListener('load', function load() {
window.removeEventListener('load', load);
});
window.addEventListener('message', receiveMessage, false);
setButtonListeners();
window.btcpay = {
showFrame: showFrame,
hideFrame: hideFrame,
showInvoice: showInvoice,
onModalWillEnter: onModalWillEnter,
onModalWillLeave: onModalWillLeave,
setApiUrlPrefix: setApiUrlPrefix
};
})();

View File

@@ -0,0 +1,11 @@
<html>
<head>
<script src="btcpay.js"></script>
<script type="text/javascript">
btcpay.setApiUrlPrefix("http://127.0.0.1:14142");
</script>
</head>
<body>
<button onclick="btcpay.showInvoice('25DKDgcB4z4PtkPJyHVYnV')">Open Modal</button>
</body>
</html>