Here's the Gurobi code to solve the optimization problem:

```python
import gurobipy as gp

# Create a new model
m = gp.Model("optimization_problem")

# Create variables
lanyards = m.addVar(vtype=gp.GRB.INTEGER, name="lanyards")
postage_stamps = m.addVar(vtype=gp.GRB.INTEGER, name="postage_stamps")
cans_of_coffee = m.addVar(vtype=gp.GRB.INTEGER, name="cans_of_coffee")

# Set objective function
m.setObjective(7.8 * lanyards + 3.65 * postage_stamps + 4.87 * cans_of_coffee, gp.GRB.MINIMIZE)

# Add constraints
m.addConstr(17 * lanyards + 6 * cans_of_coffee >= 20, "c1")
m.addConstr(5 * postage_stamps + 6 * cans_of_coffee >= 12, "c2")
m.addConstr(17 * lanyards + 5 * postage_stamps + 6 * cans_of_coffee >= 20, "c3")
m.addConstr(17 * lanyards + 5 * postage_stamps + 6 * cans_of_coffee >= 20, "c4")  # Redundant constraint
m.addConstr(12 * lanyards + 8 * cans_of_coffee >= 54, "c5")
m.addConstr(11 * postage_stamps + 8 * cans_of_coffee >= 52, "c6")
m.addConstr(12 * lanyards + 11 * postage_stamps + 8 * cans_of_coffee >= 52, "c7")
m.addConstr(lanyards - 3 * postage_stamps >= 0, "c8")
m.addConstr(17 * lanyards + 5 * postage_stamps + 6 * cans_of_coffee <= 39, "c9")
m.addConstr(12 * lanyards + 8 * cans_of_coffee <= 151, "c10")
m.addConstr(12 * lanyards + 11 * postage_stamps <= 65, "c11")
m.addConstr(11 * postage_stamps + 8 * cans_of_coffee <= 101, "c12")


# Optimize model
m.optimize()

# Print results
if m.status == gp.GRB.OPTIMAL:
    print('Obj: %g' % m.objVal)
    print('lanyards:', lanyards.x)
    print('postage_stamps:', postage_stamps.x)
    print('cans_of_coffee:', cans_of_coffee.x)
elif m.status == gp.GRB.INFEASIBLE:
    print('The model is infeasible.')
else:
    print('Optimization ended with status %d' % m.status)

```
