feat: square integration (#3962)

This commit is contained in:
Vlad Stan
2026-05-22 12:37:51 +03:00
committed by GitHub
parent 30e0522419
commit a61807a257
23 changed files with 4426 additions and 18 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+11
View File
@@ -267,6 +267,15 @@ window.localisation.en = {
webhook_events_list: 'The following events must be supported by the webhook:',
webhook_stripe_description:
'One the stripe side you must configure a webhook with a URL that points to your LNbits server.',
webhook_square_description:
'On the Square side configure a webhook pointing to this exact LNbits URL.',
square_webhook_url_hint:
'Must exactly match the Square notification URL. LNbits requires the /api/v1/callback/square path.',
access_token: 'Access Token',
location_id: 'Location ID',
square_location_id_hint:
'Square location ID to create payment links for. Use the endpoint to select sandbox or production.',
api_version: 'API Version',
payment_proof: 'Payment Proof',
update: 'Update',
update_available: 'Update {version} available!',
@@ -805,6 +814,8 @@ window.localisation.en = {
webhook_id_hint: 'PayPal webhook ID used to verify incoming events.',
webhook_paypal_description:
'On the PayPal side configure a webhook pointing to your LNbits server.',
square_webhook_signature_key_hint:
'Square webhook signature key used to verify incoming events.',
callback_success_url: 'Callback Success URL',
callback_success_url_hint:
'The user will be redirected to this URL after the payment is successful',
@@ -5,6 +5,9 @@ window.app.component('lnbits-admin-fiat-providers', {
return {
formAddStripeUser: '',
formAddPaypalUser: '',
formAddSquareUser: '',
formAddRevolutUser: '',
creatingRevolutWebhook: false,
hideInputToggle: true
}
},
@@ -20,6 +23,12 @@ window.app.component('lnbits-admin-fiat-providers', {
this.formData?.paypal_payment_webhook_url ||
this.calculateWebhookUrl('paypal')
)
},
revolutWebhookUrl() {
return (
this.formData?.revolut_payment_webhook_url ||
this.calculateWebhookUrl('revolut')
)
}
},
watch: {
@@ -58,6 +67,8 @@ window.app.component('lnbits-admin-fiat-providers', {
syncWebhookUrls() {
this.maybeSetWebhookUrl('stripe_payment_webhook_url', 'stripe')
this.maybeSetWebhookUrl('paypal_payment_webhook_url', 'paypal')
this.maybeSetWebhookUrl('square_payment_webhook_url', 'square')
this.maybeSetWebhookUrl('revolut_payment_webhook_url', 'revolut')
},
maybeSetWebhookUrl(fieldName, provider) {
if (!this.formData) {
@@ -77,6 +88,47 @@ window.app.component('lnbits-admin-fiat-providers', {
}
this.copyText(url)
},
isClearnetWebhookUrl(url) {
let parsedUrl
try {
parsedUrl = new URL(url)
} catch (e) {
return false
}
const host = parsedUrl.hostname.toLowerCase()
if (!['http:', 'https:'].includes(parsedUrl.protocol)) {
return false
}
if (
host === 'localhost' ||
host.endsWith('.localhost') ||
host.endsWith('.local') ||
host.endsWith('.onion')
) {
return false
}
if (
/^127\./.test(host) ||
/^10\./.test(host) ||
/^192\.168\./.test(host) ||
/^169\.254\./.test(host) ||
/^172\.(1[6-9]|2\d|3[0-1])\./.test(host) ||
host === '0.0.0.0' ||
host === '::1'
) {
return false
}
return true
},
notifyRevolutWebhookWarning(message) {
Quasar.Notify.create({
type: 'warning',
message,
icon: null,
closeBtn: true
})
},
addStripeAllowedUser() {
const addUser = this.formAddStripeUser || ''
if (
@@ -111,6 +163,40 @@ window.app.component('lnbits-admin-fiat-providers', {
this.formData.paypal_limits.allowed_users =
this.formData.paypal_limits.allowed_users.filter(u => u !== user)
},
addSquareAllowedUser() {
const addUser = this.formAddSquareUser || ''
if (
addUser.length &&
!this.formData.square_limits.allowed_users.includes(addUser)
) {
this.formData.square_limits.allowed_users = [
...this.formData.square_limits.allowed_users,
addUser
]
this.formAddSquareUser = ''
}
},
removeSquareAllowedUser(user) {
this.formData.square_limits.allowed_users =
this.formData.square_limits.allowed_users.filter(u => u !== user)
},
addRevolutAllowedUser() {
const addUser = this.formAddRevolutUser || ''
if (
addUser.length &&
!this.formData.revolut_limits.allowed_users.includes(addUser)
) {
this.formData.revolut_limits.allowed_users = [
...this.formData.revolut_limits.allowed_users,
addUser
]
this.formAddRevolutUser = ''
}
},
removeRevolutAllowedUser(user) {
this.formData.revolut_limits.allowed_users =
this.formData.revolut_limits.allowed_users.filter(u => u !== user)
},
checkFiatProvider(providerName) {
LNbits.api
.request('PUT', `/api/v1/fiat/check/${providerName}`)
@@ -124,6 +210,48 @@ window.app.component('lnbits-admin-fiat-providers', {
})
})
.catch(LNbits.utils.notifyApiError)
},
createRevolutWebhook() {
const webhookUrl = this.calculateWebhookUrl('revolut')
this.formData.revolut_payment_webhook_url = webhookUrl
if (!this.formData.revolut_api_secret_key) {
this.notifyRevolutWebhookWarning(
'Add your Revolut API secret key before creating a webhook.'
)
return
}
if (!this.isClearnetWebhookUrl(webhookUrl)) {
this.notifyRevolutWebhookWarning(
'Revolut webhook URL must be a clearnet URL.'
)
return
}
this.creatingRevolutWebhook = true
LNbits.api
.request('POST', '/api/v1/fiat/revolut/webhook', null, {
url: webhookUrl,
endpoint: this.formData.revolut_api_endpoint,
api_secret_key: this.formData.revolut_api_secret_key,
api_version: this.formData.revolut_api_version
})
.then(response => {
const data = response.data
this.formData.revolut_payment_webhook_url = data.url
this.formData.revolut_webhook_signing_secret = data.signing_secret
Quasar.Notify.create({
type: 'positive',
message: `Revolut webhook ${
data.already_exists ? 'already exists' : 'created'
}${data.id ? `: ${data.id}` : ''}.`,
icon: null
})
})
.catch(LNbits.utils.notifyApiError)
.finally(() => {
this.creatingRevolutWebhook = false
})
}
}
})