175 lines
		
	
	
	
		
			6.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			175 lines
		
	
	
	
		
			6.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| // License: LGPL-3.0-or-later
 | |
| const h = require('snabbdom/h')
 | |
| const R = require('ramda')
 | |
| const flyd = require('flyd')
 | |
| flyd.lift = require('flyd/module/lift')
 | |
| flyd.flatMap = require('flyd/module/flatmap')
 | |
| const request = require('../../common/request')
 | |
| const cardForm = require('../../components/card-form.es6')
 | |
| const sepaForm = require('../../components/sepa-form.es6')
 | |
| const format = require('../../common/format')
 | |
| const progressBar = require('../../components/progress-bar')
 | |
| 
 | |
| const sepaTab = 'sepa'
 | |
| const cardTab = 'credit_card'
 | |
| 
 | |
| function init(state) {
 | |
|   const payload$ = flyd.map(supp => ({card: {holder_id: supp.id, holder_type: 'Supporter'}}), state.supporter$)
 | |
|   const supporterID$ = flyd.map(supp => supp.id, state.supporter$)
 | |
|   const card$ = flyd.merge(
 | |
|     flyd.stream({})
 | |
|   , flyd.map(supp => ({name: supp.name, address_zip: supp.zip_code}), state.supporter$)) 
 | |
| 
 | |
|   state.cardForm = cardForm.init({ path: '/cards', card$, payload$ }) 
 | |
|   state.sepaForm = sepaForm.init({ supporter: supporterID$ } )
 | |
| 
 | |
|   // Set the card ID into the donation object when it is saved
 | |
|   const cardToken$ = flyd.map(R.prop('token'), state.cardForm.saved$)
 | |
|   const donationWithCardToken$ = flyd.lift(R.assoc('token'), cardToken$, state.donation$)
 | |
| 
 | |
|   // Set the sepa transfer details ID into the donation object when it is saved
 | |
|   const sepaId$ = flyd.map(R.prop('id'), state.sepaForm.saved$)
 | |
|   const donationWithSepaId$ = flyd.lift(R.assoc('direct_debit_detail_id'), sepaId$, state.donation$)
 | |
| 
 | |
|   state.donationParams$ = flyd.immediate(
 | |
|     flyd.combine((sepaParams, cardParams, activeTab) => {
 | |
|       if(activeTab() == sepaTab) {
 | |
|         return sepaParams()
 | |
|       } else if(activeTab() == cardTab) {
 | |
|         return cardParams()
 | |
|       }
 | |
|     }, [donationWithSepaId$, donationWithCardToken$, state.activePaymentTab$])
 | |
|   )
 | |
|   const donationResp$ = flyd.flatMap(postDonation, state.donationParams$)
 | |
| 
 | |
|   state.error$ = flyd.mergeAll([
 | |
|     flyd.map(R.prop('error'), flyd.filter(resp => resp.error, donationResp$))
 | |
|   , flyd.map(R.always(undefined), state.cardForm.form.submit$)
 | |
|   , flyd.map(R.always(undefined), state.sepaForm.form.submit$)
 | |
|   , state.cardForm.error$
 | |
|   , state.sepaForm.error$
 | |
|   ])
 | |
|   state.paid$ = flyd.filter(resp => !resp.error, donationResp$)
 | |
| 
 | |
|   // Control progress bar for card payment
 | |
|   state.progress$ = flyd.scanMerge([
 | |
|     [state.cardForm.form.validSubmit$, R.always({status: I18n.t('nonprofits.donate.payment.loading.checking_card'), percentage: 20})]
 | |
|   , [state.cardForm.saved$,        R.always({status: I18n.t('nonprofits.donate.payment.loading.sending_payment'), percentage: 100})]
 | |
|   , [state.cardForm.error$, R.always({hidden: true})] // Hide when an error shows up
 | |
|   , [flyd.filter(R.identity, state.error$), R.always({hidden: true})] // Hide when an error shows up
 | |
|   ], {hidden: true})
 | |
| 
 | |
|   state.loading$ = flyd.mergeAll([
 | |
|     flyd.map(R.always(true), state.cardForm.form.validSubmit$)
 | |
|   , flyd.map(R.always(true), state.sepaForm.form.validSubmit$)
 | |
|   , flyd.map(R.always(false), state.paid$) 
 | |
|   , flyd.map(R.always(false), state.cardForm.error$) 
 | |
|   , flyd.map(R.always(false), state.sepaForm.error$)
 | |
|   , flyd.map(R.always(false), state.error$)
 | |
|   ])
 | |
| 
 | |
|   // Post the gift option, if necessary
 | |
|   const paramsWithGift$ = flyd.filter(params => params.gift_option_id || params.gift_option && params.gift_option.id, state.params$)
 | |
|   const paidWithGift$ = flyd.lift(R.pair, paramsWithGift$, state.paid$)
 | |
|   flyd.map(
 | |
|     R.apply((params, result) => postGiftOption(params.gift_option_id || params.gift_option.id, result))
 | |
|   , paidWithGift$
 | |
|   )
 | |
| 
 | |
|   // post utm tracking details after donation is saved
 | |
|   flyd.map(
 | |
|     R.apply((utmParams, donationResponse) => postTracking(app.utmParams, donationResp$))
 | |
|   , state.paid$
 | |
|   )
 | |
| 
 | |
|   return state
 | |
| }
 | |
| 
 | |
| const postGiftOption = (campaign_gift_option_id, result) => {
 | |
|   return flyd.map(R.prop('body'), request({
 | |
|       path: '/campaign_gifts'
 | |
|     , method: 'post'
 | |
|     , send: {campaign_gift: {donation_id: result.json 
 | |
|         ? result.json.donation.id // for recurring
 | |
|         : result.donation.id // for one-time
 | |
|         , campaign_gift_option_id}}
 | |
|     }).load)
 | |
| }
 | |
| 
 | |
| const postTracking = (utmParams, donationResponse) => {
 | |
|   const params = R.merge(utmParams, {donation_id: donationResponse().donation.id})
 | |
| 
 | |
|   if(utmParams.utm_source || utmParams.utm_medium || utmParams.utm_content || utmParams.utm_campaign) {
 | |
|     return flyd.map(R.prop('body'), request({
 | |
|       path: `/nonprofits/${app.nonprofit_id}/tracking`
 | |
|     , method: 'post'
 | |
|     , send: params
 | |
|     }).load)
 | |
|   }
 | |
| }
 | |
| 
 | |
| var posting = false // hack switch to prevent any kind of charge double post
 | |
| // Post either a recurring or one-time donation
 | |
| const postDonation = (donation) => {
 | |
|   if(posting) return flyd.stream()
 | |
|   else posting = true
 | |
|   var prefix = `/nonprofits/${app.nonprofit_id}/`
 | |
|   var postfix = donation.recurring ? 'recurring_donations' : 'donations'
 | |
| 
 | |
|   if(donation.weekly) { 
 | |
|     donation.amount = Math.round(4.3 * donation.amount);
 | |
|   }
 | |
|   delete donation.weekly; // needs to be removed to be processed
 | |
| 
 | |
|   if(donation.recurring) donation = {recurring_donation: donation}
 | |
|   return flyd.map(R.prop('body'), request({
 | |
|     path: prefix + postfix
 | |
|   , method: 'post'
 | |
|   , send: donation
 | |
|   }).load)
 | |
| }
 | |
| 
 | |
| const paymentTabs = (state) => {
 | |
|   if(state.activePaymentTab$() == sepaTab) {
 | |
|     return payWithSepaTab(state)
 | |
|   } else if(state.activePaymentTab$() == cardTab) {
 | |
|     return payWithCardTab(state)
 | |
|   }
 | |
| }
 | |
| 
 | |
| const payWithSepaTab = state => {
 | |
|   return h('div.u-marginBottom--10', [
 | |
|     sepaForm.view(state.sepaForm)
 | |
|   ])
 | |
| }
 | |
| 
 | |
| const payWithCardTab = state => {
 | |
|   return h('div.u-marginBottom--10', [
 | |
|     cardForm.view(R.merge(state.cardForm, {error$: state.error$, hideButton: state.loading$()}))
 | |
|   , progressBar(state.progress$())
 | |
|   ])
 | |
| }
 | |
| 
 | |
| function view(state) {
 | |
|   var isRecurring = state.donation$().recurring
 | |
|   var dedic = state.dedicationData$()
 | |
|   var amountLabel = isRecurring ? ` ${I18n.t('nonprofits.donate.payment.monthly_recurring')}` : ` ${I18n.t('nonprofits.donate.payment.one_time')}`
 | |
|   var weekly="";
 | |
|   if (state.donation$().weekly) {
 | |
|     amountLabel =   amountLabel.replace(I18n.t('nonprofits.donate.amount.monthly'),I18n.t('nonprofits.donate.amount.weekly')) + "*";
 | |
|     weekly= h('div.u-centered.notice',[h("small",I18n.t('nonprofits.donate.amount.weekly_notice',{amount:(format.weeklyToMonthly(state.donation$().amount)/100.0),currency:app.currency_symbol}))]);
 | |
|   }
 | |
|   return h('div.wizard-step.payment-step', [
 | |
|     h('p.u-fontSize--18 u.marginBottom--0.u-centered.amount', [
 | |
|       h('span', app.currency_symbol + format.centsToDollars(state.donation$().amount))
 | |
|     , h('strong', amountLabel)
 | |
|     ])
 | |
|   , weekly
 | |
|   , dedic && (dedic.first_name || dedic.last_name)
 | |
|       ? h('p.u-centered', `${dedic.dedication_type === 'memory' ? I18n.t('nonprofits.donate.dedication.in_memory_label') : I18n.t('nonprofits.donate.dedication.in_honor_label')} ` + `${dedic.first_name || ''} ${dedic.last_name || ''}`)
 | |
|       : ''
 | |
|   , paymentTabs(state)
 | |
|   ])
 | |
| }
 | |
| 
 | |
| module.exports = {view, init}
 | 
