Create subscription setup page for Monero subscriptions
This commit is contained in:
parent
9a8c9c2a86
commit
2e1820c7d1
5 changed files with 206 additions and 4 deletions
39
src/api/subscriptions-monero.ts
Normal file
39
src/api/subscriptions-monero.ts
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { BigNumber } from "@ethersproject/bignumber"
|
||||||
|
|
||||||
|
import { BACKEND_URL } from "@/constants"
|
||||||
|
import { http } from "./common"
|
||||||
|
import {
|
||||||
|
formatAmount,
|
||||||
|
getPricePerMonth as _getPricePerMonth,
|
||||||
|
getPricePerSec as _getPricePerSec,
|
||||||
|
} from "./subscriptions"
|
||||||
|
import { Profile, User } from "./users"
|
||||||
|
|
||||||
|
export function getPricePerSec(pricePerMonth: number): number {
|
||||||
|
return _getPricePerSec(pricePerMonth, 12).toNumber()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPricePerMonth(pricePerSec: number): number {
|
||||||
|
const pricePerSecInt = BigNumber.from(pricePerSec)
|
||||||
|
const pricePerMonthInt = _getPricePerMonth(pricePerSecInt)
|
||||||
|
return formatAmount(pricePerMonthInt, 12).toUnsafeFloat()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function enableMoneroSubscriptions(
|
||||||
|
authToken: string,
|
||||||
|
price: number,
|
||||||
|
payoutAddress: string,
|
||||||
|
): Promise<User> {
|
||||||
|
const url = `${BACKEND_URL}/api/v1/subscriptions/enable`
|
||||||
|
const response = await http(url, {
|
||||||
|
method: "POST",
|
||||||
|
authToken,
|
||||||
|
json: { type: "monero", price, payout_address: payoutAddress },
|
||||||
|
})
|
||||||
|
const data = await response.json()
|
||||||
|
if (response.status !== 200) {
|
||||||
|
throw new Error(data.message)
|
||||||
|
} else {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,19 @@ export function getPricePerSec(
|
||||||
return pricePerMonthInt.div(SECONDS_IN_MONTH)
|
return pricePerMonthInt.div(SECONDS_IN_MONTH)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getPricePerMonth(
|
||||||
|
pricePerSec: BigNumber,
|
||||||
|
): BigNumber {
|
||||||
|
return roundBigNumber(pricePerSec.mul(SECONDS_IN_MONTH), 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatAmount(
|
||||||
|
value: BigNumber,
|
||||||
|
tokenDecimals: number,
|
||||||
|
): FixedNumber {
|
||||||
|
return FixedNumber.fromValue(value, tokenDecimals)
|
||||||
|
}
|
||||||
|
|
||||||
export async function getSubscriptionAuthorization(
|
export async function getSubscriptionAuthorization(
|
||||||
authToken: string,
|
authToken: string,
|
||||||
pricePerSec: BigNumber,
|
pricePerSec: BigNumber,
|
||||||
|
@ -139,11 +152,11 @@ export class SubscriptionConfig {
|
||||||
|
|
||||||
// Convert raw token amount to FixedNumber
|
// Convert raw token amount to FixedNumber
|
||||||
formatAmount(value: BigNumber): FixedNumber {
|
formatAmount(value: BigNumber): FixedNumber {
|
||||||
return FixedNumber.fromValue(value, this.tokenDecimals)
|
return formatAmount(value, this.tokenDecimals)
|
||||||
}
|
}
|
||||||
|
|
||||||
get pricePerMonthInt(): BigNumber {
|
get pricePerMonthInt(): BigNumber {
|
||||||
return roundBigNumber(this.price.mul(SECONDS_IN_MONTH), 4)
|
return getPricePerMonth(this.price)
|
||||||
}
|
}
|
||||||
|
|
||||||
get pricePerMonth(): FixedNumber {
|
get pricePerMonth(): FixedNumber {
|
||||||
|
|
143
src/components/SubscriptionSettingsMonero.vue
Normal file
143
src/components/SubscriptionSettingsMonero.vue
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
<template>
|
||||||
|
<div class="subscription-settings">
|
||||||
|
<div class="info" v-if="!isLoading">
|
||||||
|
<template v-if="subscriptionOption !== null">
|
||||||
|
Subscriptions are enabled
|
||||||
|
<div class="price">
|
||||||
|
{{ getPricePerMonth(subscriptionOption.price) }} XMR per month
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
Subscriptions are not enabled
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<form v-if="canEnableSubscriptions()">
|
||||||
|
<div class="price-input-group">
|
||||||
|
<label for="price">Price</label>
|
||||||
|
<input type="number" id="price" v-model="subscriptionPrice" min="0.00" step="0.01">
|
||||||
|
<span>XMR per month</span>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="payout_address"
|
||||||
|
v-model="subscriptionPayoutAddress"
|
||||||
|
placeholder="Payout address"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="btn"
|
||||||
|
:disabled="!isFormValid()"
|
||||||
|
@click.prevent="enableSubscriptions()"
|
||||||
|
>
|
||||||
|
Enable subscriptions
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<loader v-if="isLoading"></loader>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted } from "vue"
|
||||||
|
import { $, $ref } from "vue/macros"
|
||||||
|
|
||||||
|
import { getSubscriptionOptions, SubscriptionOption } from "@/api/subscriptions"
|
||||||
|
import {
|
||||||
|
enableMoneroSubscriptions,
|
||||||
|
getPricePerMonth,
|
||||||
|
getPricePerSec,
|
||||||
|
} from "@/api/subscriptions-monero"
|
||||||
|
import Loader from "@/components/Loader.vue"
|
||||||
|
import { useCurrentUser } from "@/store/user"
|
||||||
|
|
||||||
|
const { ensureAuthToken, setCurrentUser } = $(useCurrentUser())
|
||||||
|
|
||||||
|
const subscriptionPrice = $ref(0.01)
|
||||||
|
const subscriptionPayoutAddress = $ref("")
|
||||||
|
let isLoading = $ref(false)
|
||||||
|
let subscriptionOption = $ref<SubscriptionOption | null>(null)
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
isLoading = true
|
||||||
|
await loadSubscriptionConfig()
|
||||||
|
isLoading = false
|
||||||
|
})
|
||||||
|
|
||||||
|
async function loadSubscriptionConfig() {
|
||||||
|
const subscriptionOptions = await getSubscriptionOptions(ensureAuthToken())
|
||||||
|
subscriptionOption = subscriptionOptions.find((item) => {
|
||||||
|
return item.type === "monero"
|
||||||
|
}) || null
|
||||||
|
}
|
||||||
|
|
||||||
|
function canEnableSubscriptions(): boolean {
|
||||||
|
return (
|
||||||
|
!isLoading &&
|
||||||
|
subscriptionOption === null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFormValid(): boolean {
|
||||||
|
return (
|
||||||
|
getPricePerSec(subscriptionPrice) > 0 &&
|
||||||
|
subscriptionPayoutAddress.length > 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function enableSubscriptions() {
|
||||||
|
isLoading = true
|
||||||
|
const user = await enableMoneroSubscriptions(
|
||||||
|
ensureAuthToken(),
|
||||||
|
getPricePerSec(subscriptionPrice),
|
||||||
|
subscriptionPayoutAddress,
|
||||||
|
)
|
||||||
|
setCurrentUser(user)
|
||||||
|
await loadSubscriptionConfig()
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import "../styles/layout";
|
||||||
|
@import "../styles/theme";
|
||||||
|
|
||||||
|
.info {
|
||||||
|
background-color: $block-background-color;
|
||||||
|
border-radius: $block-border-radius;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $block-inner-padding / 2;
|
||||||
|
padding: $block-inner-padding;
|
||||||
|
|
||||||
|
.price {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $block-inner-padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-input-group {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
font-size: 16px;
|
||||||
|
gap: $input-padding;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -448,7 +448,6 @@ function canManageSubscriptions(): boolean {
|
||||||
// Only users with verified address can configure subscription
|
// Only users with verified address can configure subscription
|
||||||
const blockchain = instance?.blockchains[0]
|
const blockchain = instance?.blockchains[0]
|
||||||
return (
|
return (
|
||||||
Boolean(blockchain?.contract_address) &&
|
|
||||||
Boolean(blockchain?.features.subscriptions) &&
|
Boolean(blockchain?.features.subscriptions) &&
|
||||||
profile !== null &&
|
profile !== null &&
|
||||||
profile.getVerifiedEthereumAddress() !== null &&
|
profile.getVerifiedEthereumAddress() !== null &&
|
||||||
|
@ -459,7 +458,6 @@ function canManageSubscriptions(): boolean {
|
||||||
function canSubscribe(): boolean {
|
function canSubscribe(): boolean {
|
||||||
const blockchain = instance?.blockchains[0]
|
const blockchain = instance?.blockchains[0]
|
||||||
return (
|
return (
|
||||||
Boolean(blockchain?.contract_address) &&
|
|
||||||
Boolean(blockchain?.features.subscriptions) &&
|
Boolean(blockchain?.features.subscriptions) &&
|
||||||
profile !== null &&
|
profile !== null &&
|
||||||
profile.getVerifiedEthereumAddress() !== null &&
|
profile.getVerifiedEthereumAddress() !== null &&
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
<template #content>
|
<template #content>
|
||||||
<h1>Manage subscriptions</h1>
|
<h1>Manage subscriptions</h1>
|
||||||
<subscription-settings-ethereum v-if="isEthereum()"></subscription-settings-ethereum>
|
<subscription-settings-ethereum v-if="isEthereum()"></subscription-settings-ethereum>
|
||||||
|
<subscription-settings-monero v-if="isMonero()"></subscription-settings-monero>
|
||||||
</template>
|
</template>
|
||||||
</sidebar-layout>
|
</sidebar-layout>
|
||||||
</template>
|
</template>
|
||||||
|
@ -12,6 +13,7 @@ import { $, $computed } from "vue/macros"
|
||||||
|
|
||||||
import SidebarLayout from "@/components/SidebarLayout.vue"
|
import SidebarLayout from "@/components/SidebarLayout.vue"
|
||||||
import SubscriptionSettingsEthereum from "@/components/SubscriptionSettingsEthereum.vue"
|
import SubscriptionSettingsEthereum from "@/components/SubscriptionSettingsEthereum.vue"
|
||||||
|
import SubscriptionSettingsMonero from "@/components/SubscriptionSettingsMonero.vue"
|
||||||
import { useInstanceInfo } from "@/store/instance"
|
import { useInstanceInfo } from "@/store/instance"
|
||||||
|
|
||||||
const { instance } = $(useInstanceInfo())
|
const { instance } = $(useInstanceInfo())
|
||||||
|
@ -24,6 +26,13 @@ function isEthereum(): boolean {
|
||||||
}
|
}
|
||||||
return blockchain.chain_id.startsWith("eip155")
|
return blockchain.chain_id.startsWith("eip155")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isMonero(): boolean {
|
||||||
|
if (!blockchain) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return blockchain.chain_id.startsWith("monero")
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|
Loading…
Reference in a new issue