Skip to content

Commit

Permalink
Refund functionality implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
TheF1rstPancake committed Jul 19, 2016
1 parent 64b9ba4 commit da2d5d3
Show file tree
Hide file tree
Showing 4 changed files with 303 additions and 24 deletions.
78 changes: 75 additions & 3 deletions actions/checkouts.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ export const REQUEST = 'REQUEST_CHECKOUTS'
export const RECEIVE = 'RECEIVE_CHECKOUTS'
export const SEARCH = 'SEARCH_CHECKOUTS'
export const INVALIDATE = 'INVALIDATE_CHECKOUTS'
export const REFUND = "REFUND_CHECKOUT"
export const RECEIVE_REFUND = "RECEIVE_REFUND_CHECKOUT"
export const CLEAR_REFUND = "CLEAR_REFUND_STATE"

export function searchCheckout(email, account_id = null, checkout_id=null) {
return {
Expand Down Expand Up @@ -40,6 +43,43 @@ function receiveCheckout(email, account_id = null, checkout_id = null, json) {
}
}

function refundCheckout(email, checkout_id, amount = null, refund_reason) {
return {
type: REFUND,
email: email,
checkout_id: checkout_id,
amount:amount,
refund_reason:refund_reason
}
}

function receiveRefund(email, checkout_id, data) {
return {
type: RECEIVE_REFUND,
email: email,
checkout_id:checkout_id,
refund: data
}
}

function requestRefund(email, checkout_id, amount=null, refund_reason) {
return dispatch => {
dispatch(refundCheckout(email, checkout_id, amount, refund_reason))
return $.post("/refund", {"email":email, "checkout_id": checkout_id, "amount":amount, "refund_reason":refund_reason})
.fail(function(data){
console.log("ERROR: ", data);
var error_data = JSON.parse(data.responseText);
})
.done(function(data){
//dispatch receive refund action
dispatch(receiveRefund(email, checkout_id, data));

// update the checkout data for this checkout
dispatch(fetchCheckoutIfNeeded(email, null, checkout_id))
})
}
}

function fetchCheckout(email, account_id = null, checkout_id = null) {
return dispatch => {
dispatch(requestCheckout(email, account_id, checkout_id))
Expand All @@ -50,7 +90,8 @@ function fetchCheckout(email, account_id = null, checkout_id = null) {
var error_data = JSON.parse(data.responseText);
})
.done(function(data){
dispatch(receiveCheckout(email, account_id, checkout_id, data))
//dispatch the receive reach
dispatch(receiveCheckout(email, account_id, checkout_id, data));
})
}
}
Expand All @@ -65,10 +106,41 @@ function shouldFetchCheckout(state, account_id) {
return false;
}

export function fetchCheckoutIfNeeded(email, account_id) {
export function fetchCheckoutIfNeeded(email, account_id=null, checkout_id=null) {
return (dispatch, getState) => {
if (shouldFetchCheckout(getState())) {
return dispatch(fetchCheckout(email, account_id))
return dispatch(fetchCheckout(email, account_id, checkout_id))
}
}
}

function shouldRefundCheckout(state, checkout_id, amount) {
var checkout = null;
console.log("Searching for checkout_id: ", checkout_id)
for (var i =0; i < state.wepay_checkout.checkout.checkoutInfo.length; i++) {
if (state.wepay_checkout.checkout.checkoutInfo[i].checkout_id == checkout_id) {
console.log("Found checkout to refund: ", checkout);
checkout = state.wepay_checkout.checkout.checkoutInfo[i];
}
}
if (checkout && checkout.amount - checkout.refund.amount_refunded > amount) {
console.log("Should refund");
return true;
}
console.log("Should NOT refund");
return false;
}

export function fetchRefundIfNeeded(email, checkout_id, amount, refund_reason) {
return(dispatch, getState) => {
if(shouldRefundCheckout(getState(), checkout_id, amount)) {
return dispatch(requestRefund(email, checkout_id, amount, refund_reason))
}
}
}

export function clearRefund() {
return {
type: CLEAR_REFUND
}
}
127 changes: 123 additions & 4 deletions components/Checkouts.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,134 @@
import React, { PropTypes } from 'react'
import {Grid, FormGroup, FormControl, Row, Col, ControlLabel, Table} from "react-bootstrap"
import {Grid, Label, FormGroup, FormControl, Row, Col, ControlLabel, Table, Button, Modal} from "react-bootstrap"
import { connect } from 'react-redux'
import {BootstrapTable} from "react-bootstrap-table"

import {fetchRefundIfNeeded, clearRefund} from "../actions/checkouts"

import Base from "./Base"

var Checkouts = React.createClass({
getInitialState: function() {
return {
checkoutInfo: {},
showModal: false,
refund: {
selectedCheckoutId: null,
refundAmount: 0,
refundReason: "",
}
}
},
openModal: function(event) {
console.log("OPENING MODAL", event.target.id);
this.setState({showModal:true, refund:{selectedCheckoutId: event.target.id}});
},
closeModal: function() {
this.props.dispatch(clearRefund());
this.setState({showModal:false, refund:{}});
},
buildModal: function() {
var checkout = null;
for (var i = 0; i < this.props.checkoutInfo.length; i++) {
if(this.props.checkoutInfo[i].checkout_id == this.state.refund.selectedCheckoutId) {
checkout = this.props.checkoutInfo[i];
break;
}
}

// if no checkout exists, then don't build the modal
if (!checkout) {
return (<div></div>);
}

// otherwise build it
var maxRefundableAmount = (checkout.amount - checkout.refund.amount_refunded);
var successful_refund;
if (this.props.successful_refund) {
successful_refund = (<h3><Label bsStyle="success">Refund completed!</Label></h3>);
}
console.log(successful_refund);
return (
<div>
<Modal show = {this.state.showModal} onHide = {this.closeModal}>
<Modal.Header closeButton>
<Modal.Title>Refund Checkout</Modal.Title>
</Modal.Header>
<Modal.Body>
<p><strong>Max Refundable Amount:</strong> ${maxRefundableAmount}</p>
<form onSubmit={this.refundCheckout}>
<FormGroup>
<ControlLabel>Refund Amount</ControlLabel>
<FormControl
type="number"
id="refundAmount"
max={maxRefundableAmount}
required
/>
</FormGroup>
<FormGroup>
<ControlLabel>Refund Reason</ControlLabel>
<FormControl
type="text"
id="refundReason"
required/>
</FormGroup>
{successful_refund}
<Button type="submit" bsStyle="success" value="Submit Refund" disabled={this.props.submitted_refund}>Submit Refund</Button>
</form>
</Modal.Body>
</Modal>
</div>
);
},
handleAmountChange: function(event) {
var current = this.state.refund;
current.refundAmount = event.target.value;
this.setState({refund: current})
},
handleReasonChange: function(event) {
var current = this.state.refund;
current.refundReason = event.target.value;
this.setState({refund: current})
},
handleClick: function() {
refundCheckout: function(e) {
e.preventDefault();
console.log("Performing refund for: ", this.state.refund.selectedCheckoutId);
var checkout_id = this.state.refund.selectedCheckoutId;
var refundAmount = $("#refundAmount").val();
var refundReason = $("#refundReason").val();
var current = this.state.refund;
this.setState({refund:current})

this.props.dispatch(fetchRefundIfNeeded(this.props.email, checkout_id, refundAmount, refundReason));
},
formatCheckoutID: function(cell,row) {
return "<a href='#' id=" + cell + ">" + cell + "</a>";
},
formatDate: function(cell, row) {
return new Date(cell * 1000).toString();
},
formatRefund: function(cell, row) {
// cell is the refund_amount_refunded value. If this is less than the amount of the original checkout then we want to include a refund button
// if the value is greater than 0, then someone initiated a refund, so we want to include the reason for the refund and how much it was
var d = null;
var refundString = (<p><strong>Refunded</strong>: ${cell}<br></br>{row.refund_refund_reason}</p>);
var refundButton = (<Button bsStyle="primary" bsSize="small" id={row.checkout_id} onClick={this.openModal}>Refund</Button>)
if (cell > 0) {
if (cell == row.amount) {
return (<div>{refundString}</div>)
}
else {
return (<div>
{refundString}
{refundButton}
</div>);

}
}
return (<div>{refundButton}</div>);

},

serialize: function(info) {
var array = [];

Expand All @@ -29,6 +138,7 @@ var Checkouts = React.createClass({
return array;
},
render: function() {
console.log("Rendering checkouts");
if (this.props.checkoutInfo == null || $.isEmptyObject(this.props.checkoutInfo) || $.isEmptyObject(this.props.error) == false) {
return (<div></div>);
}
Expand Down Expand Up @@ -83,9 +193,16 @@ var Checkouts = React.createClass({
dataField = "payer_name"
>
Payer Name
</TableHeaderColumn>
<TableHeaderColumn
dataField = "refund_amount_refunded"
dataFormat = {this.formatRefund}
>
Refund
</TableHeaderColumn>
</BootstrapTable>
</Row>
{this.buildModal()}
</div>
);
}
Expand All @@ -96,7 +213,9 @@ const mapStateToProps = (state) => {
return {
checkoutInfo:state.wepay_checkout.checkout.checkoutInfo,
email: state.wepay_user.searchedUser,
error: state.errors.info
error: state.errors.info,
submitted_refund: state.wepay_checkout.checkout.submitted_refund,
successful_refund: state.wepay_checkout.checkout.successful_refund,
}
}

Expand Down
72 changes: 58 additions & 14 deletions reducers/checkouts.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { combineReducers } from 'redux'

import {
SEARCH, INVALIDATE,
REQUEST, RECEIVE
REQUEST, RECEIVE, REFUND, RECEIVE_REFUND, CLEAR_REFUND
} from '../actions/checkouts'

function searchedCheckout(state = {}, action) {
Expand All @@ -17,28 +17,69 @@ function searchedCheckout(state = {}, action) {
}
}

function updateSingleCheckout(checkout, action) {
return Object.assign({}, checkout, action.checkout);
}

function updateCheckout(state, action) {
console.log("Updating checkout: ", action)
for (var i = 0; i < state.checkoutInfo.length; i++) {
if(state.checkoutInfo[i].checkout_id == action.checkout_id) {
state.checkoutInfo = [
...state.checkoutInfo.slice(0, i),
updateSingleCheckout(state.checkoutInfo[i], action),
...state.checkoutInfo.slice(i+1)
];
break;
}
}
return state
}



function checkout_base(state = {
isFetching: false,
didInvalidate: false,
submitted_refund: false,
successful_refund:false,
checkoutInfo: []
}, action) {
switch (action.type) {
case INVALIDATE:
return Object.assign({}, state, {
didInvalidate: true
})
return Object.assign({}, state, {
didInvalidate: true
})
case REQUEST:
return Object.assign({}, state, {
isFetching: true,
didInvalidate: false
})
return Object.assign({}, state, {
isFetching: true,
didInvalidate: false
})
case RECEIVE:
return Object.assign({}, state, {
isFetching: false,
didInvalidate: false,
checkoutInfo: action.checkout,
lastUpdated: action.receivedAt
})
if (action.checkout_id) {
return Object.assign({}, state, updateCheckout(state, action));
}
return Object.assign({}, state, {
isFetching: false,
didInvalidate: false,
checkoutInfo: action.checkout,
lastUpdated: action.receivedAt
})
case REFUND:
return Object.assign({}, state, {
submitted_refund: true,
successful_refund:false,
})
case RECEIVE_REFUND:
return Object.assign({}, state, {
submitted_refund: false,
successful_refund:true,
})
case CLEAR_REFUND:
return Object.assign({}, state, {
submitted_refund: false,
successful_refund:false,
})
default:
return state
}
Expand All @@ -49,6 +90,9 @@ function checkout(state = {}, action) {
case INVALIDATE:
case RECEIVE:
case REQUEST:
case RECEIVE_REFUND:
case REFUND:
case CLEAR_REFUND:
return Object.assign({}, state, checkout_base(state, action))
default:
return state
Expand Down
Loading

0 comments on commit da2d5d3

Please sign in to comment.