-
Notifications
You must be signed in to change notification settings - Fork 205
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2377 from shadrak98/fc_partnership_fee
feat(partner): Partnership Fees via dashboard
- Loading branch information
Showing
29 changed files
with
1,186 additions
and
151 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
134 changes: 134 additions & 0 deletions
134
dashboard/src2/components/partners/BuyPartnerCreditsRazorpay.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
<template> | ||
<div> | ||
<span | ||
v-if="team.doc.currency === 'INR'" | ||
class="mt-2.5 inline-flex gap-2 text-base text-gray-700" | ||
> | ||
<FeatherIcon name="info" class="my-1 h-4" /> | ||
<span class="leading-5"> | ||
If you select Razorpay, you can pay using Credit Card, Debit Card, Net | ||
Banking, UPI, Wallets, etc. If you are using Net Banking, it may take | ||
upto 5 days for balance to reflect. | ||
</span> | ||
</span> | ||
<ErrorMessage class="mt-3" :message="createRazorpayOrder.error" /> | ||
<div class="mt-8"> | ||
<Button | ||
v-if="!isPaymentComplete" | ||
class="w-full" | ||
size="md" | ||
variant="solid" | ||
label="Proceed to payment using Razorpay" | ||
:loading="createRazorpayOrder.loading" | ||
@click="createRazorpayOrder.submit()" | ||
/> | ||
<Button | ||
v-else | ||
class="w-full" | ||
size="md" | ||
label="Confirming payment" | ||
variant="solid" | ||
:loading="isVerifyingPayment" | ||
/> | ||
</div> | ||
</div> | ||
</template> | ||
<script setup> | ||
import { Button, ErrorMessage, FeatherIcon, createResource } from 'frappe-ui'; | ||
import { ref, onMounted, onBeforeUnmount, inject } from 'vue'; | ||
import { toast } from 'vue-sonner'; | ||
import { DashboardError } from '../../utils/error'; | ||
const props = defineProps({ | ||
amount: { | ||
type: Number, | ||
default: 0 | ||
}, | ||
maximumAmount: { | ||
type: Number, | ||
default: 0 | ||
}, | ||
type: { | ||
type: String, | ||
default: 'prepaid-credits' | ||
} | ||
}); | ||
const emit = defineEmits(['success']); | ||
const team = inject('team'); | ||
const isPaymentComplete = ref(false); | ||
const isVerifyingPayment = ref(false); | ||
const razorpayCheckoutJS = ref(null); | ||
onMounted(() => { | ||
razorpayCheckoutJS.value = document.createElement('script'); | ||
razorpayCheckoutJS.value.setAttribute( | ||
'src', | ||
'https://checkout.razorpay.com/v1/checkout.js' | ||
); | ||
razorpayCheckoutJS.value.async = true; | ||
document.head.appendChild(razorpayCheckoutJS.value); | ||
}); | ||
onBeforeUnmount(() => { | ||
razorpayCheckoutJS.value?.remove(); | ||
}); | ||
let order_type = | ||
props.type === 'prepaid-credits' ? 'Prepaid Credits' : 'Partnership Fee'; | ||
const createRazorpayOrder = createResource({ | ||
url: 'press.api.billing.create_razorpay_order', | ||
params: { | ||
amount: props.amount, | ||
type: order_type | ||
}, | ||
onSuccess: data => processOrder(data), | ||
validate: () => { | ||
if (props.amount > props.maximumAmount) { | ||
throw new DashboardError('Amount more than maximum amount required'); | ||
} | ||
} | ||
}); | ||
const handlePaymentFailed = createResource({ | ||
url: 'press.api.billing.handle_razorpay_payment_failed', | ||
onSuccess: () => { | ||
console.log('Payment Failed.'); | ||
} | ||
}); | ||
function processOrder(data) { | ||
const options = { | ||
key: data.key_id, | ||
order_id: data.order_id, | ||
name: 'Frappe Cloud', | ||
image: 'https://frappe.io/files/cloud.png', | ||
prefill: { email: team.doc?.user }, | ||
handler: handlePaymentSuccess, | ||
theme: { color: '#171717' } | ||
}; | ||
const rzp = new Razorpay(options); | ||
// Opens the payment checkout frame | ||
rzp.open(); | ||
// Attach failure handler | ||
rzp.on('payment.failed', handlePaymentFailure); | ||
// rzp.on('payment.success', this.handlePaymentSuccess); | ||
} | ||
function handlePaymentFailure(response) { | ||
handlePaymentFailed.submit({ response }); | ||
toast.error('Payment failed'); | ||
} | ||
function handlePaymentSuccess() { | ||
isPaymentComplete.value = true; | ||
emit('success'); | ||
toast.success('Payment successful'); | ||
} | ||
</script> |
168 changes: 168 additions & 0 deletions
168
dashboard/src2/components/partners/BuyPartnerCreditsStripe.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
<template> | ||
<div> | ||
<label | ||
class="block" | ||
:class="{ | ||
'pointer-events-none h-0.5 opacity-0': step != 'Add Card Details', | ||
'mt-4': step == 'Add Card Details' | ||
}" | ||
> | ||
<span class="text-sm leading-4 text-gray-700"> | ||
Credit or Debit Card | ||
</span> | ||
<div class="form-input mt-2 block w-full pl-3" ref="cardElementRef"></div> | ||
<ErrorMessage class="mt-1" :message="cardErrorMessage" /> | ||
</label> | ||
|
||
<div v-if="step == 'Setting up Stripe'" class="mt-8 flex justify-center"> | ||
<Spinner class="h-4 w-4 text-gray-700" /> | ||
</div> | ||
<ErrorMessage | ||
class="mt-2" | ||
:message="createPaymentIntent.error || errorMessage" | ||
/> | ||
<div class="mt-8"> | ||
<Button | ||
v-if="step == 'Get Amount'" | ||
class="w-full" | ||
size="md" | ||
variant="solid" | ||
label="Proceed to payment using Stripe" | ||
:loading="createPaymentIntent.loading" | ||
@click="createPaymentIntent.submit()" | ||
/> | ||
<Button | ||
v-else-if="step == 'Add Card Details'" | ||
class="w-full" | ||
size="md" | ||
variant="solid" | ||
label="Make payment via Stripe" | ||
:loading="paymentInProgress" | ||
@click="onBuyClick" | ||
/> | ||
</div> | ||
</div> | ||
</template> | ||
<script setup> | ||
import { Button, ErrorMessage, Spinner, createResource } from 'frappe-ui'; | ||
import { loadStripe } from '@stripe/stripe-js'; | ||
import { ref, nextTick, inject } from 'vue'; | ||
import { toast } from 'vue-sonner'; | ||
import { DashboardError } from '../../utils/error'; | ||
const props = defineProps({ | ||
amount: { | ||
type: Number, | ||
default: 0 | ||
}, | ||
maximumAmount: { | ||
type: Number, | ||
default: 0 | ||
} | ||
}); | ||
const emit = defineEmits(['success']); | ||
const team = inject('team'); | ||
const step = ref('Get Amount'); | ||
const clientSecret = ref(null); | ||
const cardErrorMessage = ref(null); | ||
const errorMessage = ref(null); | ||
const paymentInProgress = ref(false); | ||
const stripe = ref(null); | ||
const card = ref(null); | ||
const elements = ref(null); | ||
const ready = ref(false); | ||
const cardElementRef = ref(null); | ||
const createPaymentIntent = createResource({ | ||
url: 'press.api.billing.create_payment_intent_for_partnership_fees', | ||
params: { amount: props.amount }, | ||
validate() { | ||
if (props.amount > props.maximumAmount) { | ||
throw new DashboardError( | ||
`Amount must be lesser than or equal to ${props.maximumAmount}` | ||
); | ||
} | ||
}, | ||
async onSuccess(data) { | ||
step.value = 'Setting up Stripe'; | ||
let { publishable_key, client_secret } = data; | ||
clientSecret.value = client_secret; | ||
stripe.value = await loadStripe(publishable_key); | ||
elements.value = stripe.value.elements(); | ||
const style = { | ||
base: { | ||
color: '#171717', | ||
fontFamily: [ | ||
'ui-sans-serif', | ||
'system-ui', | ||
'-apple-system', | ||
'BlinkMacSystemFont', | ||
'"Segoe UI"', | ||
'Roboto', | ||
'"Helvetica Neue"', | ||
'Arial', | ||
'"Noto Sans"', | ||
'sans-serif', | ||
'"Apple Color Emoji"', | ||
'"Segoe UI Emoji"', | ||
'"Segoe UI Symbol"', | ||
'"Noto Color Emoji"' | ||
].join(', '), | ||
fontSmoothing: 'antialiased', | ||
fontSize: '13px', | ||
'::placeholder': { | ||
color: '#C7C7C7' | ||
} | ||
}, | ||
invalid: { | ||
color: '#7C7C7C', | ||
iconColor: '#7C7C7C' | ||
} | ||
}; | ||
card.value = elements.value.create('card', { | ||
hidePostalCode: true, | ||
style: style, | ||
classes: { | ||
complete: '', | ||
focus: 'bg-gray-100' | ||
} | ||
}); | ||
step.value = 'Add Card Details'; | ||
nextTick(() => { | ||
card.value.mount(cardElementRef.value); | ||
}); | ||
card.value.addEventListener('change', event => { | ||
cardErrorMessage.value = event.error?.message || null; | ||
}); | ||
card.value.addEventListener('ready', () => { | ||
ready.value = true; | ||
}); | ||
} | ||
}); | ||
async function onBuyClick() { | ||
paymentInProgress.value = true; | ||
let payload = await stripe.value.confirmCardPayment(clientSecret.value, { | ||
payment_method: { card: card.value } | ||
}); | ||
if (payload.error) { | ||
errorMessage.value = payload.error.message; | ||
paymentInProgress.value = false; | ||
} else { | ||
toast.success( | ||
'Payment processed successfully, we will update your account shortly on confirmation from Stripe' | ||
); | ||
paymentInProgress.value = false; | ||
emit('success'); | ||
errorMessage.value = null; | ||
} | ||
} | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.