There's no such thing as a free lunch
JavaScript (ES6), 88 85 bytes

Takes input as an array of strings. Returns 0 for not free or 1 for free.

a=&gt;a.map(s=&gt;([a,b]=s.split` `,t+={e:+a,c:-a,t:x=t*a/100,d:-x}[(b||'e')[0]]),t=0)|t&lt;=0


How it works

Each line is split on the space to get a = amount, b = type of operation. If there's no operation at all (which is the case on the first line), b is set by default to "e" for "extra".

To add the correct amount to the total t, we use an object whose keys are the first letter of the operation:

{
  e: +a,           // extra
  c: -a,           // coupon
  t: t * a / 100,  // tip
  d: -t * a / 100  // discount
}


Note: If the bill consisted of only one element, map() would return a single-element array which would be coerced to an integer when applied the | operator, making the final test fail. But the OP confirmed that this can't happen. (Arrays of 2 or more elements are coerced to 0.)

Demo



let f =

a=&gt;a.map(s=&gt;([a,b]=s.split` `,t+={e:+a,c:-a,t:x=t*a/100,d:-x}[(b||'e')[0]]),t=0)|t&lt;=0

console.log(f([
'12.34',
'15 tip',
'25 discount',
'1.5 extra',
'2 coupon'
]))

console.log(f([
'10',
'20 tip',
'20 discount',
'2 coupon',
'2 coupon',
'1 coupon',
'50 discount',
'2.55 coupon'
]))